Repository: grapl-security/grapl (language: python)

Issue:
Update `libssl-dev` to `1.1.1n-0+deb11u3`
`1.1.1n-0+deb11u2` is no longer available.


Discussion / hints:

Fix patch:
diff --git a/.buildkite/pipeline.verify.yml b/.buildkite/pipeline.verify.yml
index b9a06f6868..f9071e6112 100644
--- a/.buildkite/pipeline.verify.yml
+++ b/.buildkite/pipeline.verify.yml
@@ -209,37 +209,40 @@ steps:
                 - grapl/CODECOV_TOKEN
           - grapl-security/codecov#v0.1.6
 
-  - label: ":hammer: Integration Tests"
-    command:
-      - make test-integration
-    plugins:
-      - grapl-security/vault-login#v0.1.3
-      - grapl-security/vault-env#v0.1.0:
-          secrets:
-            - grapl/TOOLCHAIN_AUTH_TOKEN
-    env:
-      GRAPL_LOG_LEVEL: "DEBUG"
-    agents:
-      queue: "beefy"
-    artifact_paths:
-      - "test_artifacts/**/*"
-      # https://github.com/grapl-security/issue-tracker/issues/894
-      - "src/js/engagement_view/source_map_explorer_result.html"
+  - group: "Integration tests"
+    key: integration-tests
+    steps:
+      - label: ":python: Python Integration Tests"
+        command:
+          - make test-integration-python
+        plugins:
+          - grapl-security/vault-login#v0.1.3
+          - grapl-security/vault-env#v0.1.0:
+              secrets:
+                - grapl/TOOLCHAIN_AUTH_TOKEN
+        env:
+          GRAPL_LOG_LEVEL: "DEBUG"
+        agents:
+          queue: "beefy"
+        artifact_paths:
+          - "test_artifacts/**/*"
+          # https://github.com/grapl-security/issue-tracker/issues/894
+          - "src/js/engagement_view/source_map_explorer_result.html"
 
-  - label: ':hammer: "New" Integration Tests'
-    command:
-      - make test-integration-new
-    plugins:
-      - grapl-security/vault-login#v0.1.3
-      - grapl-security/vault-env#v0.1.0:
-          secrets:
-            - grapl/TOOLCHAIN_AUTH_TOKEN
-    env:
-      GRAPL_LOG_LEVEL: "DEBUG"
-    agents:
-      queue: "beefy"
-    artifact_paths:
-      - "test_artifacts/**/*"
+      - label: ":rust: Rust Integration Tests"
+        command:
+          - make test-integration-rust
+        plugins:
+          - grapl-security/vault-login#v0.1.3
+          - grapl-security/vault-env#v0.1.0:
+              secrets:
+                - grapl/TOOLCHAIN_AUTH_TOKEN
+        env:
+          GRAPL_LOG_LEVEL: "DEBUG"
+        agents:
+          queue: "beefy"
+        artifact_paths:
+          - "test_artifacts/**/*"
 
   - label: "Build docs :book:"
     command:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index d8a10041a4..b6a58246de 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -43,7 +43,6 @@ BUILD @christophermaier
 /src/js/engagement_view/ @andrea-grapl
 /src/js/graphql_endpoint/ @andrea-grapl
 /src/proto/ @grapl-security/wg-data-infra
-/src/python/analyzer_executor/ @wimax-grapl @grapl-security/wg-data-infra
 /src/python/engagement-creator/ @wimax-grapl @grapl-security/wg-data-infra
 /src/python/graphql_endpoint_tests/ @wimax-grapl
 /src/python/grapl_analyzerlib/ @colin-grapl
@@ -54,7 +53,6 @@ BUILD @christophermaier
 /src/python/provisioner/ @jgrillo-grapl
 /src/python/python-proto/ @grapl-security/wg-data-infra
 /src/sh/ @wimax-grapl @christophermaier
-/src/rust/analyzer-dispatcher/ @grapl-security/wg-data-infra
 /src/rust/bin/ @christophermaier
 /src/rust/.cargo @christophermaier
 /src/rust/.cargo/audit.toml @colin-grapl @inickles-grapl
@@ -69,9 +67,7 @@ BUILD @christophermaier
 /src/rust/graph-merger/ @grapl-security/wg-data-infra
 /src/rust/grapl-config/ @inickles-grapl @wimax-grapl
 /src/rust/grapl-graphql-codegen/ @colin-grapl
-/src/rust/grapl/ @inickles-grapl
 /src/rust/grapl-observe/ @wimax-grapl
-/src/rust/grapl-service/ @inickles-grapl
 /src/rust/grapl-utils/ @inickles-grapl
 /src/rust/grapl-web-ui/ @inickles-grapl
 /src/rust/kafka/ @grapl-security/wg-data-infra
@@ -86,9 +82,7 @@ BUILD @christophermaier
 /src/rust/node-identifier/ @grapl-security/wg-data-infra
 /src/rust/nomad-client-gen/ @wimax-grapl
 /src/rust/rust-proto/ @grapl-security/wg-data-infra
-src/rust/rust-proto-new/ @grapl-security/wg-data-infra
 /src/rust/rust-toolchain.toml @inickles-grapl
-/src/rust/sqs-executor/ @inickles-grapl @grapl-security/wg-data-infra
 /src/rust/sysmon-parser/ @inickles-grapl
 /src/rust/uid-allocator/ @colin-grapl
 /test/docker-compose* @inickles-grapl @christophermaier
diff --git a/3rdparty/python/constraints.txt b/3rdparty/python/constraints.txt
index 97ee66b0bc..bbf850afc7 100644
--- a/3rdparty/python/constraints.txt
+++ b/3rdparty/python/constraints.txt
@@ -1,4 +1,4 @@
-# Generated by ./build-support/manage_virtualenv.sh on Fri 20 May 2022 04:15:52 PM EDT
+# Generated by ./build-support/manage_virtualenv.sh on Sat 25 Jun 2022 02:11:07 PM EDT
 # DO NOT EDIT BY HAND
 ansicolors==1.1.8
 argon2-cffi==21.3.0
@@ -8,27 +8,28 @@ arrow==1.2.2
 async-timeout==4.0.2
 attrs==21.4.0
 binaryornot==0.4.4
-boto3==1.23.5
-botocore==1.26.5
-certifi==2022.5.18.1
+boto3==1.24.17
+botocore==1.27.17
+certifi==2022.6.15
 cffi==1.15.0
-chardet==4.0.0
+chardet==5.0.0
 charset-normalizer==2.0.12
 click==7.1.2
-confluent-kafka==1.8.2
+confluent-kafka==1.9.0
 cookiecutter==1.7.3
 Deprecated==1.2.13
 dill==0.3.5.1
+exceptiongroup==1.0.0rc8
 fasteners==0.16.3
-grpcio==1.46.1
+grpcio==1.47.0
 humbug==0.2.7
-hypothesis==6.46.7
+hypothesis==6.47.4
 idna==3.3
 ijson==3.1.4
 iniconfig==1.1.1
 Jinja2==3.1.2
 jinja2-time==0.2.0
-jmespath==1.0.0
+jmespath==1.0.1
 MarkupSafe==2.1.1
 opentelemetry-api==1.11.1
 opentelemetry-exporter-zipkin-json==1.11.1
@@ -37,24 +38,24 @@ opentelemetry-instrumentation-botocore==0.30b1
 opentelemetry-sdk==1.11.1
 opentelemetry-semantic-conventions==0.30b1
 packaging==21.3
-pantsbuild.pants==2.11.1rc0
-pantsbuild.pants.testutil==2.11.1rc0
+pantsbuild.pants==2.11.1rc3
+pantsbuild.pants.testutil==2.11.1rc3
 parver==0.3.1
-pex==2.1.84
-pip==22.1
+pex==2.1.90
+pip==22.1.2
 pluggy==1.0.0
 poyo==0.5.0
-protobuf==3.20.1
+protobuf==3.15.8
 psutil==5.9.0
 pulumi==3.33.1
-pulumi-aws==5.4.0
+pulumi-aws==5.9.1
 pulumi-consul==3.6.0
 pulumi-docker==3.2.0
 pulumi-kafka==3.3.0
 pulumi-nomad==0.2.1
 pulumi-policy==1.4.0
-pulumi-postgresql==3.4.0
-pulumi-random==4.7.0
+pulumi-postgresql==3.5.0
+pulumi-random==4.8.0
 py==1.11.0
 pycparser==2.21
 pydantic==1.8.2
@@ -67,9 +68,9 @@ python-lsp-jsonrpc==1.0.0
 python-nomad==1.4.1
 python-slugify==6.1.2
 PyYAML==6.0
-redis==4.3.1
-requests==2.27.1
-s3transfer==0.5.2
+redis==4.3.3
+requests==2.28.0
+s3transfer==0.6.0
 semver==2.13.0
 setproctitle==1.2.2
 setuptools==56.0.0
diff --git a/3rdparty/python/requirements.txt b/3rdparty/python/requirements.txt
index b433438b2b..392caf914b 100644
--- a/3rdparty/python/requirements.txt
+++ b/3rdparty/python/requirements.txt
@@ -14,7 +14,7 @@ opentelemetry-exporter-zipkin-json
 opentelemetry-instrumentation
 opentelemetry-instrumentation-botocore
 opentelemetry-sdk
-protobuf>=3.12.1
+protobuf>=3.15.8,<3.16.0
 pulumi>=3.0.0,<4.0.0
 pulumi-aws>=5.0.0,<6.0.0
 pulumi_consul
diff --git a/Makefile b/Makefile
index 9e2b5d8e9d..c0c64be40a 100644
--- a/Makefile
+++ b/Makefile
@@ -39,8 +39,8 @@ endif
 # over invoking `docker buildx bake` directly.
 DOCKER_BUILDX_BAKE := docker buildx bake $(buildx_builder_args)
 
-COMPOSE_PROJECT_INTEGRATION_TESTS := grapl-integration_tests
-COMPOSE_PROJECT_INTEGRATION_TESTS_NEW := rust-integration-tests-new
+COMPOSE_PROJECT_PYTHON_INTEGRATION_TESTS := python-integration-tests
+COMPOSE_PROJECT_RUST_INTEGRATION_TESTS := rust-integration-tests
 
 # All the services defined in the docker-compose.check.yml file are
 # run with the same general arguments; just supply the service name to
@@ -114,20 +114,20 @@ help: ## Print this help
 	@printf -- '             (≡)      /____/             /_/            \n'
 	@printf -- '\n'
 	@printf -- '${FMT_BOLD}Useful environment variables (with examples):${FMT_END}\n'
-	@printf -- '  ${FMT_PURPLE}TARGETS${FMT_END}="typecheck-analyzer-executor typecheck-grapl-common" make typecheck\n'
+	@printf -- '  ${FMT_PURPLE}TARGETS${FMT_END}="typecheck-grapl-common" make typecheck\n'
 	@printf -- '    to only run a subset of test targets.\n'
 	@printf -- '\n'
-	@printf -- '  ${FMT_PURPLE}KEEP_TEST_ENV=1${FMT_END} make test-integration\n'
+	@printf -- '  ${FMT_PURPLE}KEEP_TEST_ENV=1${FMT_END} make test-integration-rust\n'
 	@printf -- '    to keep the test environment around after a test suite.\n'
 	@printf -- '\n'
-	@printf -- '  ${FMT_PURPLE}WITH_PULUMI_TRACING=1${FMT_END} makeup \n'
+	@printf -- '  ${FMT_PURPLE}WITH_PULUMI_TRACING=1${FMT_END} make up\n'
 	@printf -- '    to send pulumi traces to Jaeger (see docs/development/debugging.md).\n'
 	@printf -- '\n'
 	@printf -- '  ${FMT_PURPLE}WITH_TRACING=1${FMT_END} make build-local-infrastructure \n'
 	@printf -- '    to send docker build traces to Jaeger (see docs/development/debugging.md).\n'
 	@printf -- '\n'
 	@printf -- '  ${FMT_BOLD}FUN FACT${FMT_END}: You can also specify these as postfix, like:\n'
-	@printf -- '    make test-something KEEP_TEST_ENV=1\n'
+	@printf -- '    make test-integration-rust KEEP_TEST_ENV=1\n'
 	@printf '\n'
 	@awk 'BEGIN {FS = ":.*##"; printf "Usage: make ${FMT_BLUE}<target>${FMT_END}\n"} \
 		 /^[a-zA-Z0-9_-]+:.*?##/ { printf "  ${FMT_BLUE}%-46s${FMT_END} %s\n", $$1, $$2 } \
@@ -190,15 +190,15 @@ build-local-infrastructure: build-grapl-service-prerequisites
 	@echo "--- Building the Grapl SaaS service images and local-only images"
 	$(DOCKER_BUILDX_BAKE) local-infrastructure
 
-.PHONY: build-test-integration
-build-test-integration:
-	@echo "--- Building integration test images"
-	$(DOCKER_BUILDX_BAKE) integration-tests
+.PHONY: build-test-integration-rust
+build-test-integration-rust:
+	@echo "--- Building rust integration test images"
+	$(DOCKER_BUILDX_BAKE) rust-integration-tests
 
-.PHONY: build-test-integration-new
-build-test-integration-new:
-	@echo "--- Building \"new\" integration test images"
-	$(DOCKER_BUILDX_BAKE) rust-integration-tests-new
+.PHONY: build-test-integration-python
+build-test-integration-python:
+	@echo "--- Building python integration test images"
+	$(DOCKER_BUILDX_BAKE) python-integration-tests
 
 ########################################################################
 
@@ -289,19 +289,19 @@ test-unit-rust-coverage: ## Run Rust unit tests and gather coverage statistics (
 typecheck: ## Typecheck Python Code
 	./pants check ::
 
-.PHONY: test-integration
-test-integration: build-local-infrastructure
-test-integration: build-test-integration
-test-integration: export COMPOSE_PROJECT_NAME := $(COMPOSE_PROJECT_INTEGRATION_TESTS)
-test-integration: ## Build and run integration tests
-	$(MAKE) test-with-env EXEC_TEST_COMMAND="nomad/bin/run_parameterized_job.sh integration-tests 9"
+.PHONY: test-integration-python
+test-integration-python: build-local-infrastructure
+test-integration-python: build-test-integration-python
+test-integration-python: export COMPOSE_PROJECT_NAME := $(COMPOSE_PROJECT_PYTHON_INTEGRATION_TESTS)
+test-integration-python: ## Build and run python integration tests
+	$(MAKE) test-with-env EXEC_TEST_COMMAND="nomad/bin/run_parameterized_job.sh python-integration-tests 9"
 
-.PHONY: test-integration-new
-test-integration-new: build-local-infrastructure
-test-integration-new: build-test-integration-new
-test-integration-new: export COMPOSE_PROJECT_NAME := $(COMPOSE_PROJECT_INTEGRATION_TESTS_NEW)
-test-integration-new: ## Build and run "new" integration tests
-	$(MAKE) test-with-env EXEC_TEST_COMMAND="nomad/bin/run_parameterized_job.sh integration-tests-new 9"
+.PHONY: test-integration-rust
+test-integration-rust: build-local-infrastructure
+test-integration-rust: build-test-integration-rust
+test-integration-rust: export COMPOSE_PROJECT_NAME := $(COMPOSE_PROJECT_RUST_INTEGRATION_TESTS)
+test-integration-rust: ## Build and run rust integration tests
+	$(MAKE) test-with-env EXEC_TEST_COMMAND="nomad/bin/run_parameterized_job.sh rust-integration-tests 9"
 
 # This target is not intended to be used directly from the command line.
 # Think of it as a Context Manager that:
@@ -472,7 +472,8 @@ down: ## docker compose down - both stops and removes the containers
 	# LAMBDA_DOCKER_NETWORK environment variable.
 	$(MAKE) stop-nomad-detach
 	docker compose $(EVERY_COMPOSE_FILE) down --timeout=0
-	@docker compose $(EVERY_COMPOSE_FILE) --project-name $(COMPOSE_PROJECT_INTEGRATION_TESTS) down --timeout=0
+	@docker compose $(EVERY_COMPOSE_FILE) --project-name $(COMPOSE_PROJECT_RUST_INTEGRATION_TESTS) down --timeout=0
+	@docker compose $(EVERY_COMPOSE_FILE) --project-name $(COMPOSE_PROJECT_PYTHON_INTEGRATION_TESTS) down --timeout=0
 
 .PHONY: stop
 stop: ## docker compose stop - stops (but preserves) the containers
diff --git a/bin/upload_image_to_cloudsmith.sh b/bin/upload_image_to_cloudsmith.sh
index 5014dcddf5..f028453182 100755
--- a/bin/upload_image_to_cloudsmith.sh
+++ b/bin/upload_image_to_cloudsmith.sh
@@ -3,7 +3,7 @@
 ########################################################################
 # Example Usage
 #
-# bin/upload_image_to_cloudsmith.sh integration-tests-new:dev
+# bin/upload_image_to_cloudsmith.sh rust-integration-tests:dev
 ########################################################################
 
 set -euo pipefail
diff --git a/docker-bake.hcl b/docker-bake.hcl
index 48f3fd6642..4007368ea9 100644
--- a/docker-bake.hcl
+++ b/docker-bake.hcl
@@ -139,14 +139,13 @@ group "cloudsmith-images" {
   # NOTE: Please keep this list sorted in alphabetical order
   targets = [
     "grapl-services",
-    "rust-integration-tests-new"
+    "rust-integration-tests"
   ]
 }
 
 group "rust-services" {
   # NOTE: Please keep this list sorted in alphabetical order
   targets = [
-    "analyzer-dispatcher",
     "event-source",
     "generator-dispatcher",
     "generator-executor",
@@ -167,7 +166,6 @@ group "rust-services" {
 group "python-services" {
   # NOTE: Please keep this list sorted in alphabetical order
   targets = [
-    "analyzer-executor",
     "engagement-creator",
     "provisioner"
   ]
@@ -201,19 +199,18 @@ group "local-infrastructure" {
   ]
 }
 
-group "integration-tests" {
+group "python-integration-tests" {
   # NOTE: Please keep this list sorted in alphabetical order
   targets = [
     "python-integration-tests",
-    "rust-integration-tests"
   ]
 }
 
 group "all-tests" {
   # NOTE: Please keep this list sorted in alphabetical order
   targets = [
-    "integration-tests",
-    "rust-integration-tests-new"
+    "python-integration-tests",
+    "rust-integration-tests"
   ]
 }
 
@@ -275,14 +272,6 @@ target "_rust-base" {
   }
 }
 
-target "analyzer-dispatcher" {
-  inherits = ["_rust-base"]
-  target   = "analyzer-dispatcher-deploy"
-  tags = [
-    upstream_aware_tag("analyzer-dispatcher")
-  ]
-}
-
 target "generator-dispatcher" {
   inherits = ["_rust-base"]
   target   = "generator-dispatcher-deploy"
@@ -420,14 +409,6 @@ target "_python-base" {
   dockerfile = "src/python/Dockerfile"
 }
 
-target "analyzer-executor" {
-  inherits = ["_python-base"]
-  target   = "analyzer-executor-deploy"
-  tags = [
-    upstream_aware_tag("analyzer-executor")
-  ]
-}
-
 target "engagement-creator" {
   inherits = ["_python-base"]
   target   = "engagement-creator-deploy"
@@ -471,18 +452,10 @@ target "python-integration-tests" {
 target "rust-integration-tests" {
   inherits = ["_rust-base"]
   target   = "integration-tests"
-  tags = [
-    local_only_tag("rust-integration-tests")
-  ]
-}
-
-target "rust-integration-tests-new" {
-  inherits = ["_rust-base"]
-  target   = "integration-tests-new"
   tags = [
     # Yes, we push this up to Cloudsmith to run tests against AWS
     # infrastructure; that's why we use `upstream_aware_tag`.
-    upstream_aware_tag("rust-integration-tests-new")
+    upstream_aware_tag("rust-integration-tests")
   ]
 }
 
diff --git a/docker-compose.yml b/docker-compose.yml
index 5a681bd3b9..0dd280ade7 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -31,11 +31,19 @@ services:
         # Basically: It's mostly harmless.
 
         (
-          cd integration_tests
+          cd python_integration_tests
           # Ideally this stack name would be scoped by organization, but
           # login --local doesn't support organizations
-          pulumi stack init local-grapl-integration-tests --non-interactive
-          pulumi up --yes --skip-preview --stack=local-grapl-integration-tests $$TRACING_ARGS
+          pulumi stack init local-grapl-python-integration-tests --non-interactive
+          pulumi up --yes --skip-preview --stack=local-grapl-python-integration-tests $$TRACING_ARGS
+        )
+
+        (
+          cd rust_integration_tests
+          # Ideally this stack name would be scoped by organization, but
+          # login --local doesn't support organizations
+          pulumi stack init local-grapl-rust-integration-tests --non-interactive
+          pulumi up --yes --skip-preview --stack=local-grapl-rust-integration-tests $$TRACING_ARGS
         )
 
     volumes:
diff --git a/docs/analyzers/deploying.md b/docs/analyzers/deploying.md
deleted file mode 100644
index 3328192589..0000000000
--- a/docs/analyzers/deploying.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Deploying Analyzers
-
-Once you've written your Analyzers you'll want to deploy them to Grapl.
-
-To upload the analyzer file to the bucket where it lives, use the
-`graplctl upload analyzer` tool, which works both locally and with AWS.
-
-## Deploying from Github
-
-We can keep our detection logic in Github, which will allow us to perform code
-reviews, linting, and automate the deployment of our analyzers.
-
-As an example,
-[insanitybit/grapl-analyzers](https://github.com/insanitybit/grapl-analyzers) is
-set up to use this webhook.
diff --git a/docs/development/services.md b/docs/development/services.md
index af2d1103ca..7bb73f87a7 100644
--- a/docs/development/services.md
+++ b/docs/development/services.md
@@ -39,20 +39,6 @@ de-duplicate this process node.
 **Output:** TODO (it looks like exactly the same as node identifier's output,
 just re-encoded?)
 
-### Analyzer Dispatcher
-
-**Work:** Determines which analyzers to execute against recently merged graphs
-
-**Output:** TODO
-
-### Analyzer Executor
-
-**Work:** Executes queries against Dgraph, and - upon a successful query hit -
-determines which lenses and risk nodes to create
-
-**Output:** A JSON dump of risks and lenses to be created by the Engagement
-Creator (see `analyzer_executor.py#emit_event` for specifics)
-
 ### Engagement Creator
 
 **Work:** Simply appends the risks and lenses described in its input to Dgraph.
diff --git a/docs/plugins/models.md b/docs/plugins/models.md
index f7d902bc61..36178488c2 100644
--- a/docs/plugins/models.md
+++ b/docs/plugins/models.md
@@ -168,7 +168,4 @@ Run the notebook and you should be good to go.
 
 ## Deploying Analyzers With Plugins
 
-The simplest way to using Plugins in your Analyzers is to publish them to the
-PyPI and then add them as requirements to the
-`analyzer_executor/requirements.txt`, rebuild, and redeploy. At that point your
-analyzers can import the plugins and you can build out your graph signatures.
+TBD
diff --git a/docs/setup/aws.md b/docs/setup/aws.md
index aa92465834..02b08a5d40 100644
--- a/docs/setup/aws.md
+++ b/docs/setup/aws.md
@@ -119,28 +119,11 @@ and execute the end-to-end tests in AWS.
 
 ### Deploy analyzers
 
-To deploy the test analyzers, run the following `graplctl` commands:
-
-```bash
-./bin/graplctl upload analyzer --analyzer_main_py etc/local_grapl/unique_cmd_parent/main.py
-```
-
-```bash
-./bin/graplctl upload analyzer --analyzer_main_py etc/local_grapl/suspicious_svchost/main.py
-```
-
-Now Grapl is ready to analyze a test dataset.
+TBD
 
 ### Upload test data
 
-To upload the test data, run the following `graplctl` command:
-
-```bash
-./bin/graplctl upload sysmon --logfile etc/sample_data/eventlog.xml
-```
-
-This will send the test dataset to the appropriate location in S3, which will
-kick off the Grapl data pipeline.
+TBD
 
 ### Logging in to the Grapl UI with the test user
 
diff --git a/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow.py b/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow.py
index 75b9d85c2e..6d4e73caea 100644
--- a/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow.py
+++ b/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow.py
@@ -11,8 +11,6 @@
     "sysmon-generator.stdout",
     "node-identifier.stdout",
     "graph-merger.stdout",
-    "analyzer-dispatcher.stdout",
-    "analyzer-executor.stdout",
     "engagement-creator.stdout",
 ]
 
diff --git a/etc/local_grapl/suspicious_svchost/BUILD b/etc/local_grapl/suspicious_svchost/BUILD
deleted file mode 100644
index 404711f449..0000000000
--- a/etc/local_grapl/suspicious_svchost/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources(skip_mypy=True)
diff --git a/etc/local_grapl/suspicious_svchost/main.py b/etc/local_grapl/suspicious_svchost/main.py
deleted file mode 100644
index f6082435d0..0000000000
--- a/etc/local_grapl/suspicious_svchost/main.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from typing import Any
-
-from grapl_analyzerlib.analyzer import Analyzer, OneOrMany
-from grapl_analyzerlib.execution import ExecutionHit
-from grapl_analyzerlib.prelude import AssetQuery, Not, ProcessQuery, ProcessView
-
-
-class SuspiciousSvchost(Analyzer):
-    def get_queries(self) -> OneOrMany[ProcessQuery]:
-        invalid_parents = [
-            Not("services.exe"),
-            Not("smss.exe"),
-            Not("ngentask.exe"),
-            Not("userinit.exe"),
-            Not("GoogleUpdate.exe"),
-            Not("conhost.exe"),
-            Not("MpCmdRun.exe"),
-        ]
-
-        return (
-            ProcessQuery()
-            .with_process_name(eq=invalid_parents)
-            .with_children(ProcessQuery().with_process_name(eq="svchost.exe"))
-            .with_asset(AssetQuery().with_hostname())
-        )
-
-    def on_response(self, response: ProcessView, output: Any):
-        asset_id = response.get_asset().get_hostname()
-
-        output.send(
-            ExecutionHit(
-                analyzer_name="Suspicious svchost",
-                node_view=response,
-                risk_score=75,
-                lenses=[("hostname", asset_id), ("analyzer_name", "SuspiciousSvchost")],
-                risky_node_keys=[
-                    # the asset and the process
-                    response.get_asset().node_key,
-                    response.node_key,
-                ],
-            )
-        )
diff --git a/etc/local_grapl/unique_cmd_parent/BUILD b/etc/local_grapl/unique_cmd_parent/BUILD
deleted file mode 100644
index 404711f449..0000000000
--- a/etc/local_grapl/unique_cmd_parent/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources(skip_mypy=True)
diff --git a/etc/local_grapl/unique_cmd_parent/main.py b/etc/local_grapl/unique_cmd_parent/main.py
deleted file mode 100644
index 3f349e862e..0000000000
--- a/etc/local_grapl/unique_cmd_parent/main.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from typing import Any, Type
-
-from grapl_analyzerlib.analyzer import A, Analyzer, OneOrMany
-from grapl_analyzerlib.counters import ParentChildCounter
-from grapl_analyzerlib.execution import ExecutionHit
-from grapl_analyzerlib.prelude import (
-    AssetQuery,
-    GraphClient,
-    Not,
-    ProcessQuery,
-    ProcessView,
-)
-
-
-class RareParentOfCmd(Analyzer):
-    def __init__(self, graph_client: GraphClient, counter: ParentChildCounter):
-        super(RareParentOfCmd, self).__init__(graph_client)
-        self.counter = counter
-
-    @classmethod
-    def build(cls: Type[A], graph_client: GraphClient) -> A:
-        counter = ParentChildCounter(graph_client)
-        return RareParentOfCmd(graph_client, counter)
-
-    def get_queries(self) -> OneOrMany[ProcessQuery]:
-        # TODO: We should be checking binary paths for these to ensure we handle impersonation
-        parent_whitelist = [
-            Not("svchost.exe"),
-            Not("RuntimeBroker.exe"),
-            Not("chrome.exe"),
-            Not("explorer.exe"),
-            Not("SIHClient.exe"),
-            Not("conhost.exe"),
-            Not("MpCmdRun.exe"),
-            Not("GoogleUpdateComRegisterShell64.exe"),
-            Not("GoogleUpdate.exe"),
-            Not("notepad.exe"),
-            Not("OneDrive.exe"),
-            Not("VBoxTray.exe"),
-            Not("Firefox Installer.exe"),
-        ]
-
-        return (
-            ProcessQuery()
-            .with_process_name(eq=parent_whitelist)
-            .with_children(ProcessQuery().with_process_name(eq="cmd.exe"))
-            .with_asset(AssetQuery().with_hostname())
-        )
-
-    def on_response(self, response: ProcessView, output: Any) -> None:
-        count = self.counter.get_count_for(
-            parent_process_name=response.get_process_name(),
-            child_process_name="cmd.exe",
-        )
-
-        asset_id = response.get_asset().get_hostname()
-
-        if count <= 3:
-            output.send(
-                ExecutionHit(
-                    analyzer_name="Rare Parent of cmd.exe",
-                    node_view=response,
-                    risk_score=10,
-                    lenses=[("hostname", asset_id)],
-                )
-            )
diff --git a/nomad/bin/run_parameterized_job.sh b/nomad/bin/run_parameterized_job.sh
index 98cd387b6e..027129fa23 100755
--- a/nomad/bin/run_parameterized_job.sh
+++ b/nomad/bin/run_parameterized_job.sh
@@ -5,8 +5,8 @@
 # Works on localhost:4646 by default, but that can be overridden with NOMAD_ADDRESS=
 #
 # Example usage:
-#  nomad/bin/run_parameterized_job.sh integration-tests-new 6
-#  NOMAD_ADDRESS=http://cool.domain:4646 nomad/bin/run_parameterized_job.sh integration-tests 7
+#  nomad/bin/run_parameterized_job.sh rust-integration-tests 6
+#  NOMAD_ADDRESS=http://cool.domain:4646 nomad/bin/run_parameterized_job.sh rust-integration-tests 7
 ##########
 
 set -euo pipefail
diff --git a/nomad/consul-intentions/dgraph-alpha-0-grpc-public.json b/nomad/consul-intentions/dgraph-alpha-0-grpc-public.json
index b6ad4c781f..acf6e17735 100644
--- a/nomad/consul-intentions/dgraph-alpha-0-grpc-public.json
+++ b/nomad/consul-intentions/dgraph-alpha-0-grpc-public.json
@@ -2,12 +2,6 @@
   "Kind": "service-intentions",
   "Name": "dgraph-alpha-0-grpc-public",
   "Sources": [
-    {
-      "Name": "analyzer-executor",
-      "Action": "allow",
-      "Precedence": 9,
-      "Type": "consul"
-    },
     {
       "Name": "engagement-creator",
       "Action": "allow",
diff --git a/nomad/grapl-core.nomad b/nomad/grapl-core.nomad
index 597de1d9f8..0fc2049efd 100644
--- a/nomad/grapl-core.nomad
+++ b/nomad/grapl-core.nomad
@@ -33,11 +33,6 @@ variable "aws_region" {
   type = string
 }
 
-variable "analyzer_bucket" {
-  type        = string
-  description = "The s3 bucket which the analyzer stores items to analyze"
-}
-
 variable "dgraph_replicas" {
   type    = number
   default = 1
@@ -108,6 +103,11 @@ variable "organization_management_db" {
   description = "Vars for organization-management database"
 }
 
+variable "organization_management_healthcheck_polling_interval_ms" {
+  type        = string
+  description = "The amount of time to wait between each healthcheck execution."
+}
+
 variable "pipeline_ingress_healthcheck_polling_interval_ms" {
   type        = string
   description = "The amount of time to wait between each healthcheck execution."
@@ -817,108 +817,6 @@ job "grapl-core" {
     }
   }
 
-  group "analyzer-dispatcher" {
-
-    task "analyzer-dispatcher" {
-      driver = "docker"
-
-      config {
-        image = var.container_images["analyzer-dispatcher"]
-      }
-
-      template {
-        data        = var.aws_env_vars_for_local
-        destination = "aws-env-vars-for-local.env"
-        env         = true
-      }
-
-      env {
-        # AWS vars
-        AWS_REGION = var.aws_region
-        # rust vars
-        RUST_LOG       = var.rust_log
-        RUST_BACKTRACE = local.rust_backtrace
-        # service vars
-        GRAPL_ANALYZERS_BUCKET = var.analyzer_bucket
-        DEST_BUCKET_NAME       = "fake"
-        SOURCE_QUEUE_URL       = "fake"
-        DEAD_LETTER_QUEUE_URL  = "fake"
-        # tracing vars
-        OTEL_EXPORTER_JAEGER_AGENT_HOST = local.tracing_jaeger_endpoint_host
-        OTEL_EXPORTER_JAEGER_AGENT_PORT = local.tracing_jaeger_endpoint_port
-      }
-
-      service {
-        name = "analyzer-dispatcher"
-      }
-
-    }
-
-  }
-
-  group "analyzer-executor" {
-    network {
-      mode = "bridge"
-      dns {
-        servers = local.dns_servers
-      }
-    }
-
-    task "analyzer-executor" {
-      driver = "docker"
-
-      config {
-        image = var.container_images["analyzer-executor"]
-      }
-
-      template {
-        data        = var.aws_env_vars_for_local
-        destination = "aws-env-vars-for-local.env"
-        env         = true
-      }
-
-      env {
-        # AWS vars
-        AWS_DEFAULT_REGION = var.aws_region
-        # python vars
-        GRAPL_LOG_LEVEL = var.py_log_level
-        # dgraph vars
-        MG_ALPHAS = local.alpha_grpc_connect_str
-        # service vars
-        GRAPL_ANALYZER_MATCHED_SUBGRAPHS_BUCKET = "fake"
-        GRAPL_ANALYZERS_BUCKET                  = var.analyzer_bucket
-        GRAPL_MODEL_PLUGINS_BUCKET              = var.model_plugins_bucket
-        SOURCE_QUEUE_URL                        = "fake"
-        GRPC_ENABLE_FORK_SUPPORT                = 1
-        HITCACHE_ADDR                           = local.redis_host
-        HITCACHE_PORT                           = local.redis_port
-        IS_RETRY                                = "False"
-        MESSAGECACHE_ADDR                       = local.redis_host
-        MESSAGECACHE_PORT                       = local.redis_port
-        OTEL_EXPORTER_ZIPKIN_ENDPOINT           = local.tracing_zipkin_endpoint
-      }
-    }
-
-    service {
-      name = "analyzer-executor"
-      connect {
-        sidecar_service {
-          proxy {
-            dynamic "upstreams" {
-              iterator = alpha
-              for_each = local.dgraph_alphas
-
-              content {
-                destination_name = "dgraph-alpha-${alpha.value.id}-grpc-public"
-                local_bind_port  = alpha.value.grpc_public_port
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
   group "engagement-creator" {
     network {
       mode = "bridge"
@@ -1206,6 +1104,8 @@ job "grapl-core" {
         ORGANIZATION_MANAGEMENT_DB_USERNAME  = var.organization_management_db.username
         OTEL_EXPORTER_JAEGER_AGENT_HOST      = local.tracing_jaeger_endpoint_host
         OTEL_EXPORTER_JAEGER_AGENT_PORT      = local.tracing_jaeger_endpoint_port
+
+        ORGANIZATION_MANAGEMENT_HEALTHCHECK_POLLING_INTERVAL_MS = var.organization_management_healthcheck_polling_interval_ms
       }
     }
 
diff --git a/nomad/lib/nomad_cli_tools.sh b/nomad/lib/nomad_cli_tools.sh
index 873d336f6a..15a0103fcb 100644
--- a/nomad/lib/nomad_cli_tools.sh
+++ b/nomad/lib/nomad_cli_tools.sh
@@ -40,7 +40,7 @@ nomad_stop_job() {
 
 nomad_get_per_task_results() {
     # Returns something like {
-    #   "analyzer-executor-integration-tests": {
+    #   "graph-merger-integration-tests": {
     #     "Complete": 1
     #   },
     #   "graphql-endpoint-tests": {
@@ -52,8 +52,8 @@ nomad_get_per_task_results() {
     jq_filter_out_zero_fields=$(
         cat << EOF
         .JobSummary.Summary | to_entries | map({
-            key, 
-            value: .value | to_entries | map(select(.value > 0)) | from_entries 
+            key,
+            value: .value | to_entries | map(select(.value > 0)) | from_entries
         }) | from_entries
 EOF
     )
diff --git a/pants.toml b/pants.toml
index abb04095c2..87d3265d5a 100644
--- a/pants.toml
+++ b/pants.toml
@@ -51,6 +51,12 @@ enabled = true
 # Randomly generated with `uuidgen --random`
 repo_id = "f416be7b-e109-4915-8eba-940bcfa310c1"
 
+[protoc]
+version = "3.15.8"
+known_versions = [
+    "3.15.8|linux_x86_64|b9ff821d2a4f9e9943dc2a13e6a76d99c7472dac46ddd3718a3a4c3b877c044a|1639343"
+]
+
 # Given how our code is currently structured, with package and test
 # code for every project in their own directory, I believe we have to
 # list each of those top-level containing directories here in order
@@ -67,8 +73,6 @@ root_patterns = [
   "/pants-plugins",
   "/pulumi",
   "/src/proto",
-  "/src/python/analyzer_executor/src",
-  "/src/python/analyzer_executor/tests",
   "/src/python/engagement-creator",
   "/src/python/graphql_endpoint_tests",
   "/src/python/grapl_analyzerlib",
@@ -78,6 +82,7 @@ root_patterns = [
   "/src/python/provisioner",
   "/src/python/python-proto",
   "/src/python/repl",
+  "src/python/suspicious_svchost",
 ]
 
 [python]
diff --git a/pulumi/grapl/__main__.py b/pulumi/grapl/__main__.py
index e4d351bff1..74cf9451fe 100644
--- a/pulumi/grapl/__main__.py
+++ b/pulumi/grapl/__main__.py
@@ -64,8 +64,6 @@ def _container_images(artifacts: ArtifactGetter) -> Mapping[str, DockerImageId]:
     )
 
     return {
-        "analyzer-dispatcher": builder.build_with_tag("analyzer-dispatcher"),
-        "analyzer-executor": builder.build_with_tag("analyzer-executor"),
         "dgraph": DockerImageId("dgraph/dgraph:v21.03.1"),
         "engagement-creator": builder.build_with_tag("engagement-creator"),
         "event-source": builder.build_with_tag("event-source"),
@@ -155,22 +153,16 @@ def main() -> None:
         confluent_environment_name=pulumi_config.require("confluent-environment-name"),
     )
 
-    analyzers_bucket = Bucket("analyzers-bucket", sse=True)
-    pulumi.export("analyzers-bucket", analyzers_bucket.bucket)
     model_plugins_bucket = Bucket("model-plugins-bucket", sse=False)
     plugin_registry_bucket = Bucket("plugin-registry-bucket", sse=True)
 
     all_plugin_buckets = [
-        analyzers_bucket,
         model_plugins_bucket,
         plugin_registry_bucket,
     ]
 
     pipeline_ingress_healthcheck_polling_interval_ms = "5000"
-    pulumi.export(
-        "pipeline-ingress-healthcheck-polling-interval-ms",
-        pipeline_ingress_healthcheck_polling_interval_ms,
-    )
+    organization_management_healthcheck_polling_interval_ms = "5000"
 
     firecracker_s3objs: FirecrackerS3BucketObjectsProtocol = (
         MockFirecrackerS3BucketObjects()
@@ -214,7 +206,6 @@ def main() -> None:
 
     # These are shared across both local and prod deployments.
     nomad_inputs: Final[NomadVars] = dict(
-        analyzer_bucket=analyzers_bucket.bucket,
         aws_env_vars_for_local=aws_env_vars_for_local,
         aws_region=aws.get_region().name,
         container_images=_container_images(artifacts),
@@ -223,6 +214,7 @@ def main() -> None:
         kafka_credentials=kafka_service_credentials,
         kafka_consumer_groups=kafka_consumer_groups,
         model_plugins_bucket=model_plugins_bucket.bucket,
+        organization_management_healthcheck_polling_interval_ms=organization_management_healthcheck_polling_interval_ms,
         pipeline_ingress_healthcheck_polling_interval_ms=pipeline_ingress_healthcheck_polling_interval_ms,
         plugin_registry_bucket_aws_account_id=config.AWS_ACCOUNT_ID,
         plugin_registry_bucket_name=plugin_registry_bucket.bucket,
@@ -415,8 +407,6 @@ def main() -> None:
 
         for bucket in all_plugin_buckets:
             bucket.grant_put_permission_to(nomad_agent_role)
-            # Analyzer Dispatcher needs to be able to ListObjects on Analyzers
-            # Analyzer Executor needs to be able to ListObjects on Model Plugins
             bucket.grant_get_and_list_to(nomad_agent_role)
 
         cache = Cache(
diff --git a/pulumi/infra/config.py b/pulumi/infra/config.py
index 2e558c66fc..575edf897a 100644
--- a/pulumi/infra/config.py
+++ b/pulumi/infra/config.py
@@ -71,7 +71,8 @@ def _validate_stack_name() -> None:
 
 LOCAL_GRAPL: Final[bool] = STACK_NAME in (
     "local-grapl",
-    "local-grapl-integration-tests",
+    "local-grapl-python-integration-tests",
+    "local-grapl-rust-integration-tests",
 )
 # (We have a different one for integration tests because `pulumi login --local`
 #  doesn't allow for stack name conflicts, even across projects.)
diff --git a/scylladb/Dockerfile b/scylladb/Dockerfile
index ce6e2cd3cd..9d0856dbf3 100644
--- a/scylladb/Dockerfile
+++ b/scylladb/Dockerfile
@@ -5,7 +5,7 @@ ENV HWLOC_VERSION="2.7.1"
 # hadolint ignore=DL3015
 RUN apt-get update \
     && apt-get -y install curl=7.74.0-1.3+deb11u1 \
-    libssl-dev=1.1.1n-0+deb11u2 \
+    libssl-dev=1.1.1n-0+deb11u3 \
     ca-certificates=20210119  \
     gcc=4:10.2.1-1 \
     cmake=3.18.4-2+deb11u1 \
diff --git a/src/js/graphql_endpoint/Dockerfile b/src/js/graphql_endpoint/Dockerfile
index 995fcc0de4..995dcbc7e9 100644
--- a/src/js/graphql_endpoint/Dockerfile
+++ b/src/js/graphql_endpoint/Dockerfile
@@ -6,7 +6,7 @@ RUN apt-get update \
     && apt-get --yes install --no-install-recommends \
         build-essential=12.9 \
         libffi-dev=3.3-6 \
-        libssl-dev=1.1.1n-0+deb11u2 \
+        libssl-dev=1.1.1n-0+deb11u3 \
         python3=3.9.2-3 \
         zip=3.0-12 \
     && rm -rf /var/lib/apt/lists/* \
diff --git a/src/proto/graplinc/grapl/api/graph/v1beta1/types.proto b/src/proto/graplinc/grapl/api/graph/v1beta1/types.proto
index 75a9e9bc34..de97d5a759 100644
--- a/src/proto/graplinc/grapl/api/graph/v1beta1/types.proto
+++ b/src/proto/graplinc/grapl/api/graph/v1beta1/types.proto
@@ -212,3 +212,31 @@ message MergedGraph {
   // The mapping of node_key to the EdgeLists
   map<string, MergedEdgeList> edges = 2;
 }
+
+// A Lens.
+message Lens {
+  // type
+  string lens_type = 1;
+  // name
+  string lens_name = 2;
+  // uid (absent prior to engagement-creator)
+  optional uint64 uid = 3;
+  // score (absent prior to engagement-creator)
+  optional uint64 score = 4;
+}
+
+// Result computed by an analyzer when it makes a detection.
+message ExecutionHit {
+  // The mapping of node_key to the MergedNode
+  map<string, graplinc.grapl.api.graph.v1beta1.MergedNode> nodes = 1;
+  // The mapping of node_key to the EdgeLists
+  map<string, graplinc.grapl.api.graph.v1beta1.MergedEdgeList> edges = 2;
+  // The name of the analyzer which made the detection
+  string analyzer_name = 3;
+  // The risk score associated with this detection
+  uint64 risk_score = 4;
+  // Fragmentary lenses (only have type and name set)
+  repeated Lens lenses = 5;
+  // The nodes which contributed to the risk score
+  repeated string risky_node_keys = 6;
+}
diff --git a/src/proto/graplinc/grapl/pipeline/v1beta1/types.proto b/src/proto/graplinc/grapl/pipeline/v1beta1/types.proto
index 1544c6de9c..6cfb7cad58 100644
--- a/src/proto/graplinc/grapl/pipeline/v1beta1/types.proto
+++ b/src/proto/graplinc/grapl/pipeline/v1beta1/types.proto
@@ -25,19 +25,6 @@ message Metadata {
   graplinc.common.v1beta1.Uuid event_source_id = 6;
 }
 
-// A wrapper message for pipeline data. Includes useful metadata to help
-// services determine what they should do with the inner_message.
-message Envelope {
-  // Metadata about this message
-  Metadata metadata = 1;
-
-  // Fully-qualified name of the protobuf used to serialize the inner_message
-  string inner_type = 2;
-
-  // The serialized bytes of the inner message
-  bytes inner_message = 3;
-}
-
 // This is the inner_message for an Envelope published to the raw-logs topic.
 message RawLog {
   // A raw log event (to be parsed by a generator plugin)
diff --git a/src/python/Dockerfile b/src/python/Dockerfile
index 7321b9414a..5a40446182 100644
--- a/src/python/Dockerfile
+++ b/src/python/Dockerfile
@@ -20,14 +20,6 @@ USER grapl
 WORKDIR /home/grapl
 ENTRYPOINT ["/bin/bash", "-o", "errexit", "-o", "nounset", "-c"]
 
-# analyzer-executor
-################################################################################
-FROM grapl-python-base AS analyzer-executor-deploy
-# Named context support https://github.com/hadolint/hadolint/issues/830
-# hadolint ignore=DL3022
-COPY --from=dist-ctx analyzer-executor.pex .
-CMD ["./analyzer-executor.pex"]
-
 # engagement-creator
 ################################################################################
 FROM grapl-python-base AS engagement-creator-deploy
@@ -52,7 +44,6 @@ RUN apt-get update \
     && apt-get -y install --no-install-recommends \
         build-essential=12.9 \
         curl=7.74.0-1.3+deb11u1 \
-        protobuf-compiler=3.12.4-1 \
         unzip=6.0-26 \
     && rm -rf /var/lib/apt/lists/* \
     && adduser \
diff --git a/src/python/analyzer_executor/src/BUILD b/src/python/analyzer_executor/src/BUILD
deleted file mode 100644
index aef59c4d4e..0000000000
--- a/src/python/analyzer_executor/src/BUILD
+++ /dev/null
@@ -1,13 +0,0 @@
-python_sources(
-    name="analyzer_executor",
-    dependencies=[
-        "./analyzer_executor_lib",
-        "./plugins",
-    ],
-)
-
-pex_binary(
-    output_path="analyzer-executor.pex",
-    entry_point="./run.py:run",
-    tags=["service-pex"],
-)
diff --git a/src/python/analyzer_executor/src/analyzer_executor_lib/BUILD b/src/python/analyzer_executor/src/analyzer_executor_lib/BUILD
deleted file mode 100644
index db46e8d6c9..0000000000
--- a/src/python/analyzer_executor/src/analyzer_executor_lib/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources()
diff --git a/src/python/analyzer_executor/src/analyzer_executor_lib/analyzer_executor.py b/src/python/analyzer_executor/src/analyzer_executor_lib/analyzer_executor.py
deleted file mode 100644
index f71ae3b9a7..0000000000
--- a/src/python/analyzer_executor/src/analyzer_executor_lib/analyzer_executor.py
+++ /dev/null
@@ -1,447 +0,0 @@
-from __future__ import annotations
-
-import base64
-import hashlib
-import inspect
-import json
-import os
-import sys
-import traceback
-from collections import defaultdict
-from logging import Logger
-from multiprocessing import Pipe, Process
-from multiprocessing.connection import Connection
-from multiprocessing.pool import ThreadPool
-from pathlib import Path
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Dict,
-    Iterable,
-    Iterator,
-    List,
-    Mapping,
-    Optional,
-    cast,
-)
-
-import boto3
-import grapl_analyzerlib.counters  # noqa: F401
-from analyzer_executor_lib.redis_cache import EitherCache, construct_redis_client
-from grapl_analyzerlib.analyzer import Analyzer
-from grapl_analyzerlib.execution import ExecutionComplete, ExecutionFailed, ExecutionHit
-from grapl_analyzerlib.grapl_client import GraphClient
-from grapl_analyzerlib.nodes.base import BaseView
-from grapl_analyzerlib.plugin_retriever import load_plugins
-from grapl_analyzerlib.queryable import Queryable
-from grapl_analyzerlib.subgraph_view import SubgraphView
-from grapl_common.env_helpers import S3ResourceFactory
-from grapl_common.grapl_logger import get_module_grapl_logger
-from grapl_common.metrics.metric_reporter import MetricReporter, TagPair
-from grapl_common.sqs.sqs_types import S3PutRecordDict, SQSMessageBody
-from python_proto.pipeline import Metadata, OldEnvelope
-
-if TYPE_CHECKING:
-    from mypy_boto3_s3 import S3ServiceResource
-
-
-# Set up logger (this is for the whole file, including static methods)
-LOGGER = get_module_grapl_logger()
-
-# Set up plugins dir for models
-MODEL_PLUGINS_DIR = os.getenv("MODEL_PLUGINS_DIR", "/tmp")
-sys.path.insert(0, MODEL_PLUGINS_DIR)
-
-# Ensure plugins dir exists
-try:
-    directory = Path(MODEL_PLUGINS_DIR + "/model_plugins/")
-    directory.mkdir(parents=True, exist_ok=True)
-except Exception as e:
-    LOGGER.error("Failed to create model plugins directory", e)
-
-
-def verbose_cast_to_int(input: Optional[str]) -> Optional[int]:
-    if not input:
-        return None
-
-    try:
-        return int(input)
-    except (TypeError, ValueError):
-        raise ValueError(f"Couldn't cast this env variable into an int: {input}")
-
-
-class AnalyzerExecutor:
-
-    # constants
-    CHUNK_SIZE_RETRY: int = 10
-    CHUNK_SIZE_DEFAULT: int = 100
-
-    def __init__(
-        self,
-        model_plugins_bucket: str,
-        analyzers_bucket: str,
-        analyzer_matched_subgraphs_bucket: str,
-        message_cache: EitherCache,
-        hit_cache: EitherCache,
-        chunk_size: int,
-        logger: Logger,
-        metric_reporter: MetricReporter,
-    ) -> None:
-        self.model_plugins_bucket = model_plugins_bucket
-        self.analyzers_bucket = analyzers_bucket
-        self.analyzer_matched_subgraphs_bucket = analyzer_matched_subgraphs_bucket
-        self.message_cache = message_cache
-        self.hit_cache = hit_cache
-        self.chunk_size = chunk_size
-        self.logger = logger
-        self.metric_reporter = metric_reporter
-
-    @classmethod
-    def from_env(cls, env: Optional[Mapping[str, str]] = None) -> AnalyzerExecutor:
-        env = env or os.environ
-
-        # If we're retrying, change the chunk size
-        is_retry = bool(env.get("IS_RETRY", False))
-        if is_retry:
-            chunk_size = cls.CHUNK_SIZE_RETRY
-        else:
-            chunk_size = cls.CHUNK_SIZE_DEFAULT
-
-        # Set up message cache
-        messagecache_addr = env.get("MESSAGECACHE_ADDR")
-        messagecache_port: Optional[int] = verbose_cast_to_int(
-            env.get("MESSAGECACHE_PORT")
-        )
-        message_cache = construct_redis_client(messagecache_addr, messagecache_port)
-
-        # Set up hit cache
-        hitcache_addr = env.get("HITCACHE_ADDR")
-        hitcache_port: Optional[int] = verbose_cast_to_int(env.get("HITCACHE_PORT"))
-        hit_cache = construct_redis_client(hitcache_addr, hitcache_port)
-
-        metric_reporter = MetricReporter.create("analyzer-executor")
-
-        model_plugins_bucket = env["GRAPL_MODEL_PLUGINS_BUCKET"]
-        analyzers_bucket = env["GRAPL_ANALYZERS_BUCKET"]
-        analyzer_matched_subgraphs_bucket = env[
-            "GRAPL_ANALYZER_MATCHED_SUBGRAPHS_BUCKET"
-        ]
-
-        return AnalyzerExecutor(
-            model_plugins_bucket=model_plugins_bucket,
-            analyzers_bucket=analyzers_bucket,
-            analyzer_matched_subgraphs_bucket=analyzer_matched_subgraphs_bucket,
-            message_cache=message_cache,
-            hit_cache=hit_cache,
-            chunk_size=chunk_size,
-            logger=LOGGER,
-            metric_reporter=metric_reporter,
-        )
-
-    def check_caches(
-        self, file_hash: str, msg_id: str, node_key: str, analyzer_name: str
-    ) -> bool:
-        with self.metric_reporter.histogram_ctx("analyzer-executor.check_caches"):
-            if self.check_msg_cache(file_hash, node_key, msg_id):
-                self.logger.debug("cache hit - already processed")
-                return True
-
-            if self.check_hit_cache(analyzer_name, node_key):
-                self.logger.debug("cache hit - already matched")
-                return True
-
-            return False
-
-    def to_event_hash(self, components: Iterable[str]) -> str:
-        joined = ",".join(components)
-        event_hash = hashlib.sha256(joined.encode()).hexdigest()
-        return event_hash
-
-    def check_msg_cache(self, file: str, node_key: str, msg_id: str) -> bool:
-        event_hash = self.to_event_hash((file, node_key, msg_id))
-        return bool(self.message_cache.get(event_hash))
-
-    def update_msg_cache(self, file: str, node_key: str, msg_id: str) -> None:
-        event_hash = self.to_event_hash((file, node_key, msg_id))
-        self.message_cache.set(event_hash, "1")
-
-    def check_hit_cache(self, file: str, node_key: str) -> bool:
-        event_hash = self.to_event_hash((file, node_key))
-        return bool(self.hit_cache.get(event_hash))
-
-    def update_hit_cache(self, file: str, node_key: str) -> None:
-        event_hash = self.to_event_hash((file, node_key))
-        self.hit_cache.set(event_hash, "1")
-
-    async def handle_events(self, events: SQSMessageBody, context: Any) -> None:
-        # Parse sns message
-        self.logger.debug(f"handling events: {events} context: {context}")
-
-        client = GraphClient()
-
-        s3 = S3ResourceFactory(boto3).from_env()
-
-        load_plugins(
-            self.model_plugins_bucket,
-            s3.meta.client,
-            os.path.abspath(MODEL_PLUGINS_DIR),
-        )
-
-        for event in events["Records"]:
-            data = parse_s3_event(s3, event)
-
-            # FIXME: this code assumes inner_message is json
-            envelope = OldEnvelope.deserialize(data)
-            message = json.loads(envelope.inner_message)
-
-            LOGGER.info(f'Executing Analyzer: {message["key"]}')
-
-            with self.metric_reporter.histogram_ctx(
-                "analyzer-executor.download_s3_file"
-            ):
-                analyzer = download_s3_file(
-                    s3,
-                    self.analyzers_bucket,
-                    message["key"],
-                ).decode("utf8")
-            analyzer_name = message["key"].split("/")[-2]
-
-            subgraph = SubgraphView.from_proto(client, bytes(message["subgraph"]))
-
-            # TODO: Validate signature of S3 file
-            LOGGER.info(f"event {event} {envelope.metadata}")
-            rx: Connection
-            tx: Connection
-            rx, tx = Pipe(duplex=False)
-            p = Process(
-                target=self.execute_file,
-                args=(analyzer_name, analyzer, subgraph, tx, "", self.chunk_size),
-            )
-
-            p.start()
-
-            for exec_hit in self.poll_process(rx=rx, analyzer_name=analyzer_name):
-                with self.metric_reporter.histogram_ctx(
-                    "analyzer-executor.emit_event.ms",
-                    (TagPair("analyzer_name", exec_hit.analyzer_name),),
-                ):
-                    emit_event(
-                        self.analyzer_matched_subgraphs_bucket,
-                        s3,
-                        exec_hit,
-                        envelope.metadata,
-                    )
-                self.update_msg_cache(analyzer, exec_hit.root_node_key, message["key"])
-                self.update_hit_cache(analyzer_name, exec_hit.root_node_key)
-
-            p.join()
-
-    def poll_process(
-        self,
-        rx: Connection,
-        analyzer_name: str,
-    ) -> Iterator[ExecutionHit]:
-        """
-        Keep polling the spawned Process, and yield any ExecutionHits.
-        (This will probably disappear if Analyzers move to Docker images.)
-        """
-        t = 0
-
-        while True:
-            p_res = rx.poll(timeout=5)
-            if not p_res:
-                t += 1
-                LOGGER.info(
-                    f"Analyzer {analyzer_name} polled for for {t * 5} seconds without result"
-                )
-                continue
-
-            result: Optional[Any] = rx.recv()
-            if isinstance(result, ExecutionComplete):
-                self.logger.info(f"Analyzer {analyzer_name} execution complete")
-                return
-
-            # emit any hits to an S3 bucket
-            if isinstance(result, ExecutionHit):
-                self.logger.info(
-                    f"Analyzer {analyzer_name} emitting event for:"
-                    f"{result.analyzer_name} {result.root_node_key}"
-                )
-                yield result
-
-            assert not isinstance(
-                result, ExecutionFailed
-            ), f"Analyzer {analyzer_name} failed."
-
-    def exec_analyzers(
-        self,
-        dg_client: GraphClient,
-        file: str,
-        msg_id: str,
-        nodes: List[BaseView],
-        analyzers: Dict[str, Analyzer],
-        sender: Any,
-    ) -> None:
-        if not analyzers:
-            self.logger.warning("Received empty dict of analyzers")
-            return
-
-        if not nodes:
-            self.logger.warning("Received empty array of nodes")
-
-        for node in nodes:
-            querymap: Dict[str, List[Queryable]] = defaultdict(list)
-
-            for an_name, analyzer in analyzers.items():
-                if self.check_caches(file, msg_id, node.node_key, an_name):
-                    continue
-
-                queries = analyzer.get_queries()
-                if isinstance(queries, list) or isinstance(queries, tuple):
-                    querymap[an_name].extend(queries)
-                else:
-                    querymap[an_name].append(queries)
-
-            for an_name, queries in querymap.items():
-                analyzer = analyzers[an_name]
-
-                for query in queries:
-                    # TODO: Whether it was a hit or not is a good Tag
-                    tags = (TagPair("analyzer_name", an_name),)
-                    with self.metric_reporter.histogram_ctx(
-                        "analyzer-executor.query_first.ms", tags
-                    ):
-                        response = query.query_first(
-                            dg_client, contains_node_key=node.node_key
-                        )
-                    if response:
-                        self.logger.debug(
-                            f"Analyzer '{an_name}' received a hit, executing on_response()"
-                        )
-                        with self.metric_reporter.histogram_ctx(
-                            "analyzer-executor.on_response.ms", tags
-                        ):
-                            analyzer.on_response(response, sender)
-
-    def execute_file(
-        self,
-        name: str,
-        file: str,
-        graph: SubgraphView,
-        sender: Connection,
-        msg_id: str,
-        chunk_size: int,
-    ) -> None:
-        try:
-            pool = ThreadPool(processes=4)
-
-            exec(file, globals())
-            client = GraphClient()
-
-            analyzers = get_analyzer_objects(client)
-            if not analyzers:
-                self.logger.warning(f"Got no analyzers for file: {name}")
-
-            self.logger.info(f"Executing analyzers: {[an for an in analyzers.keys()]}")
-
-            for nodes in chunker([n for n in graph.node_iter()], chunk_size):
-                self.logger.info(f"Querying {len(nodes)} nodes")
-
-                def exec_analyzer(
-                    nodes: List[BaseView], sender: Connection
-                ) -> List[BaseView]:
-                    try:
-                        self.exec_analyzers(
-                            client, file, msg_id, nodes, analyzers, sender
-                        )
-
-                        return nodes
-                    except Exception as e:
-                        self.logger.error(traceback.format_exc())
-                        self.logger.error(
-                            f"Execution of {name} failed with {e} {e.args}"
-                        )
-                        sender.send(ExecutionFailed())
-                        raise
-
-                pool.apply_async(exec_analyzer, args=(nodes, sender))
-
-            pool.close()
-
-            pool.join()
-
-            sender.send(ExecutionComplete())
-
-        except Exception as e:
-            self.logger.error(traceback.format_exc())
-            self.logger.error(f"Execution of {name} failed with {e} {e.args}")
-            sender.send(ExecutionFailed())
-            raise
-
-
-def parse_s3_event(s3: S3ServiceResource, event: S3PutRecordDict) -> bytes:
-    try:
-        bucket = event["s3"]["bucket"]["name"]
-        key = event["s3"]["object"]["key"]
-    except KeyError:
-        LOGGER.error("Could not parse s3 event: {}", exc_info=True)
-        raise
-    return download_s3_file(s3, bucket, key)
-
-
-def download_s3_file(s3: S3ServiceResource, bucket: str, key: str) -> bytes:
-    obj = s3.Object(bucket, key)
-    return cast(bytes, obj.get()["Body"].read())
-
-
-def is_analyzer(analyzer_name: str, analyzer_cls: type) -> bool:
-    if analyzer_name == "Analyzer":  # This is the base class
-        return False
-    return (
-        hasattr(analyzer_cls, "get_queries")
-        and hasattr(analyzer_cls, "build")
-        and hasattr(analyzer_cls, "on_response")
-    )
-
-
-def get_analyzer_objects(dgraph_client: GraphClient) -> Dict[str, Analyzer]:
-    clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass)
-    return {
-        an[0]: an[1].build(dgraph_client)
-        for an in clsmembers
-        if is_analyzer(an[0], an[1])
-    }
-
-
-def chunker(seq: List[BaseView], size: int) -> List[List[BaseView]]:
-    return [seq[pos : pos + size] for pos in range(0, len(seq), size)]
-
-
-def emit_event(
-    analyzer_matched_subgraphs_bucket: str,
-    s3: S3ServiceResource,
-    event: ExecutionHit,
-    metadata: Metadata,
-) -> None:
-    LOGGER.info(f"emitting event for: {event.analyzer_name, event.nodes}")
-
-    meta_dict = {
-        "trace_id": str(metadata.trace_id),
-    }
-
-    event_s = json.dumps(
-        {
-            "nodes": json.loads(event.nodes),
-            "edges": json.loads(event.edges),
-            "analyzer_name": event.analyzer_name,
-            "risk_score": event.risk_score,
-            "lenses": event.lenses,
-            "risky_node_keys": event.risky_node_keys,
-            "metadata": meta_dict,
-        }
-    )
-    event_hash = hashlib.sha256(event_s.encode())
-    key = base64.urlsafe_b64encode(event_hash.digest()).decode("utf-8")
-
-    obj = s3.Object(analyzer_matched_subgraphs_bucket, key)
-    obj.put(Body=event_s.encode("utf-8"))
diff --git a/src/python/analyzer_executor/src/analyzer_executor_lib/redis_cache.py b/src/python/analyzer_executor/src/analyzer_executor_lib/redis_cache.py
deleted file mode 100644
index d19b3a284b..0000000000
--- a/src/python/analyzer_executor/src/analyzer_executor_lib/redis_cache.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from typing import Optional, Union
-
-import redis
-from grapl_common.grapl_logger import get_module_grapl_logger
-
-LOGGER = get_module_grapl_logger()
-
-
-def construct_redis_client(addr: Optional[str], port: Optional[int]) -> redis.Redis:
-    if addr and port:
-        LOGGER.debug(f"connecting to redis at {addr}:{port}")
-        return redis.Redis(host=addr, port=port, db=0)
-    else:
-        raise ValueError(f"Failed connecting to redis | addr:\t{addr} | port:\t{port}")
-
-
-class NopCache(object):
-    def set(self, key: str, value: str) -> None:
-        pass
-
-    def get(self, key: str) -> bool:
-        return False
-
-    def delete(self, key: str) -> None:
-        pass
-
-
-EitherCache = Union[NopCache, redis.Redis]
diff --git a/src/python/analyzer_executor/src/plugins/BUILD b/src/python/analyzer_executor/src/plugins/BUILD
deleted file mode 100644
index db46e8d6c9..0000000000
--- a/src/python/analyzer_executor/src/plugins/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources()
diff --git a/src/python/analyzer_executor/src/plugins/__init__.py b/src/python/analyzer_executor/src/plugins/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/python/analyzer_executor/src/run.py b/src/python/analyzer_executor/src/run.py
deleted file mode 100644
index a8f845212c..0000000000
--- a/src/python/analyzer_executor/src/run.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import asyncio
-import os
-
-import boto3
-from analyzer_executor_lib.analyzer_executor import AnalyzerExecutor
-from grapl_common.debugger.vsc_debugger import wait_for_vsc_debugger
-from grapl_common.env_helpers import SQSClientFactory
-from grapl_common.grapl_logger import get_module_grapl_logger
-from grapl_common.grapl_tracer import get_tracer
-from grapl_common.sqs.event_retriever import EventRetriever
-from grapl_common.sqs.sqs_timeout_manager import (
-    SqsTimeoutManager,
-    SqsTimeoutManagerException,
-)
-
-wait_for_vsc_debugger(service="analyzer_executor")
-
-LOGGER = get_module_grapl_logger()
-TRACER = get_tracer(service_name="analyzer_executor", module_name=__name__)
-
-
-async def _main() -> None:
-    """
-    Some TODOs to bring this inline with sqs-executor in Rust:
-    - add the shortcut-to-DEAD_LETTER_QUEUE_URL case
-    - pull the manual eventing out of `handle_events` and into an EventEmitter (maybe?)
-    """
-    with TRACER.start_as_current_span(__name__):
-        queue_url = os.environ["SOURCE_QUEUE_URL"]
-        retriever = EventRetriever(queue_url=queue_url)
-        sqs_client = SQSClientFactory(boto3).from_env()
-        analyzer_executor = AnalyzerExecutor.from_env()
-
-        for sqs_message in retriever.retrieve():
-            # We'd feed this coroutine into the timeout manager.
-            message_handle_coroutine = analyzer_executor.handle_events(
-                sqs_message.body, {}
-            )
-            # While we're waiting for that future to complete, keep telling SQS
-            # "hey, we're working on it" so it doesn't become visible on the
-            # input queue again.
-            timeout_manager = SqsTimeoutManager(
-                sqs_client=sqs_client,
-                queue_url=queue_url,
-                receipt_handle=sqs_message.receipt_handle,
-                message_id=sqs_message.message_id,
-                coroutine=message_handle_coroutine,
-            )
-            try:
-                await timeout_manager.keep_alive()
-            except SqsTimeoutManagerException:
-                LOGGER.error("SQS Timeout Manager exception", exc_info=True)
-
-
-asyncio.run(_main())
diff --git a/src/python/engagement-creator/engagement_creator/lib/engagement_creator.py b/src/python/engagement-creator/engagement_creator/lib/engagement_creator.py
index 7329a436ef..b9c5161e60 100644
--- a/src/python/engagement-creator/engagement_creator/lib/engagement_creator.py
+++ b/src/python/engagement-creator/engagement_creator/lib/engagement_creator.py
@@ -228,7 +228,7 @@ def _process_one_event(
     incident_graph = json.loads(data)
 
     """
-    The `incident_graph` dict is emitted from analyzer-executor.py#emit_event
+    The `incident_graph` dict was emitted from analyzer-executor.py#emit_event
     """
     analyzer_name = incident_graph["analyzer_name"]
     nodes_raw: Dict[str, Any] = incident_graph[
diff --git a/src/python/grapl-common/grapl_common/debugger/vsc_debugger.py b/src/python/grapl-common/grapl_common/debugger/vsc_debugger.py
index a6648b731e..2c48a8f6d3 100644
--- a/src/python/grapl-common/grapl_common/debugger/vsc_debugger.py
+++ b/src/python/grapl-common/grapl_common/debugger/vsc_debugger.py
@@ -16,7 +16,6 @@
 
 ServiceIdentifier = Literal[
     "grapl_e2e_tests",
-    "analyzer_executor",
     "graphql_endpoint_tests",
 ]
 
diff --git a/src/python/grapl_analyzerlib/grapl_analyzerlib/subgraph_view.py b/src/python/grapl_analyzerlib/grapl_analyzerlib/subgraph_view.py
index 3636b2da7d..daf122a1e5 100644
--- a/src/python/grapl_analyzerlib/grapl_analyzerlib/subgraph_view.py
+++ b/src/python/grapl_analyzerlib/grapl_analyzerlib/subgraph_view.py
@@ -5,7 +5,7 @@
     MutableMapping,
 )
 
-from graplinc.grapl.api.graph.v1beta1.types_pb2 import MergedEdgeList, MergedGraph
+from python_proto.api import MergedEdgeList, MergedGraph
 from grapl_analyzerlib.grapl_client import GraphClient
 
 
@@ -17,15 +17,13 @@ def __init__(
         self.edges = edges
 
     @staticmethod
-    def from_proto(graph_client: GraphClient, s: bytes) -> SubgraphView:
+    def from_proto(graph_client: GraphClient, subgraph: MergedGraph) -> SubgraphView:
         from grapl_analyzerlib.view_from_proto import view_from_proto
 
-        subgraph = MergedGraph()
-        subgraph.ParseFromString(s)
-
         nodes = {
             k: view_from_proto(graph_client, node) for k, node in subgraph.nodes.items()
         }
+
         return SubgraphView(nodes, subgraph.edges)
 
     def node_iter(self) -> Iterator[BaseView]:
diff --git a/src/python/grapl_analyzerlib/grapl_analyzerlib/view_from_proto.py b/src/python/grapl_analyzerlib/grapl_analyzerlib/view_from_proto.py
index 8d18833487..b6f7511ab9 100644
--- a/src/python/grapl_analyzerlib/grapl_analyzerlib/view_from_proto.py
+++ b/src/python/grapl_analyzerlib/grapl_analyzerlib/view_from_proto.py
@@ -2,7 +2,7 @@
 from grapl_analyzerlib.prelude import (
     BaseView,
 )
-from graplinc.grapl.api.graph.v1beta1.types_pb2 import MergedNode
+from python_proto.api import MergedNode
 
 
 def view_from_proto(graph_client: GraphClient, node: MergedNode) -> BaseView:
diff --git a/src/python/graplctl/graplctl/cli.py b/src/python/graplctl/graplctl/cli.py
index f18261d93c..7572413207 100644
--- a/src/python/graplctl/graplctl/cli.py
+++ b/src/python/graplctl/graplctl/cli.py
@@ -16,7 +16,6 @@
 from graplctl import __version__, common
 from graplctl.aws.commands import aws
 from graplctl.common import State
-from graplctl.upload.commands import upload
 
 Tag = common.Tag
 Ec2Instance = common.Ec2Instance
@@ -102,4 +101,3 @@ def main(
 
 
 main.add_command(aws)
-main.add_command(upload)
diff --git a/src/python/graplctl/graplctl/upload/BUILD b/src/python/graplctl/graplctl/upload/BUILD
deleted file mode 100644
index db46e8d6c9..0000000000
--- a/src/python/graplctl/graplctl/upload/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources()
diff --git a/src/python/graplctl/graplctl/upload/__init__.py b/src/python/graplctl/graplctl/upload/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/python/graplctl/graplctl/upload/commands.py b/src/python/graplctl/graplctl/upload/commands.py
deleted file mode 100644
index 9bf8096728..0000000000
--- a/src/python/graplctl/graplctl/upload/commands.py
+++ /dev/null
@@ -1,124 +0,0 @@
-from __future__ import annotations
-
-from os import PathLike
-from pathlib import Path
-
-import click
-from grapl_tests_common.upload_logs import upload_osquery_logs, upload_sysmon_logs
-from graplctl import idempotency_checks
-from graplctl.common import State, pass_graplctl_state
-from graplctl.upload.lib import upload_analyzer
-
-
-@click.group()
-@click.pass_context
-@pass_graplctl_state
-def upload(
-    graplctl_state: State,
-    ctx: click.Context,
-) -> None:
-    """commands like "upload analyzer" or "upload sysmon logs" """
-    # TODO: Disallow any uploads until we've confirmed we've provisioned
-    # https://github.com/grapl-security/issue-tracker/issues/340
-    assert idempotency_checks.is_grapl_provisioned(
-        dynamodb=graplctl_state.dynamodb,
-        schema_table=graplctl_state.schema_table,
-    ), "You can't upload anything to grapl until it's provisioned."
-
-
-@upload.command()
-@click.option(
-    "--analyzer_main_py",
-    type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
-    required=True,
-    help="Path to the analyzer's `main.py`",
-)
-@click.option(
-    "--analyzers-bucket",
-    help="Name of the S3 bucket to upload analyzers to",
-    type=click.STRING,
-    required=True,
-    envvar="GRAPL_ANALYZERS_BUCKET",
-)
-@pass_graplctl_state
-def analyzer(
-    graplctl_state: State, analyzers_bucket: str, analyzer_main_py: PathLike
-) -> None:
-    """Upload an analyzer to the S3 bucket"""
-    upload_analyzer(
-        graplctl_state.s3,
-        analyzers_bucket=analyzers_bucket,
-        analyzer_main_py=Path(analyzer_main_py).resolve(),
-    )
-
-
-@upload.command()
-@click.option(
-    "--logfile",
-    type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
-    required=True,
-    help="The log file to upload",
-)
-@click.option(
-    "--log-bucket",
-    help="The name of the S3 bucket to which Sysmon logs should be uploaded",
-    type=click.STRING,
-    required=True,
-    envvar="GRAPL_SYSMON_LOG_BUCKET",
-)
-@click.option(
-    "--queue-url",
-    help="The URL of the SQS queue for Sysmon logs",
-    type=click.STRING,
-    required=True,
-    envvar="GRAPL_SYSMON_GENERATOR_QUEUE",
-)
-@pass_graplctl_state
-def sysmon(
-    graplctl_state: State, logfile: PathLike, log_bucket: str, queue_url: str
-) -> None:
-    """Upload a Sysmon log file to the S3 bucket"""
-    upload_sysmon_logs(
-        s3_client=graplctl_state.s3,
-        sqs_client=graplctl_state.sqs,
-        deployment_name=graplctl_state.stack_name,
-        log_bucket=log_bucket,
-        queue_url=queue_url,
-        logfile=Path(logfile).resolve(),
-    )
-
-
-@upload.command()
-@click.option(
-    "--logfile",
-    type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
-    required=True,
-    help="The log file to upload",
-)
-@click.option(
-    "--log-bucket",
-    help="The name of the S3 bucket to which OSQuery logs should be uploaded",
-    type=click.STRING,
-    required=True,
-    envvar="GRAPL_OSQUERY_LOG_BUCKET",
-)
-@click.option(
-    "--queue-url",
-    help="The URL of the SQS queue for OSQuery logs",
-    type=click.STRING,
-    required=True,
-    envvar="GRAPL_OSQUERY_GENERATOR_QUEUE",
-)
-@pass_graplctl_state
-def osquery(
-    graplctl_state: State, logfile: PathLike, log_bucket: str, queue_url: str
-) -> None:
-    """Upload an OSQuery log file to the S3 bucket"""
-    upload_osquery_logs(
-        s3_client=graplctl_state.s3,
-        sqs_client=graplctl_state.sqs,
-        deployment_name=graplctl_state.stack_name,
-        log_bucket=log_bucket,
-        queue_url=queue_url,
-        logfile=Path(logfile).resolve(),
-    )
diff --git a/src/python/graplctl/graplctl/upload/lib.py b/src/python/graplctl/graplctl/upload/lib.py
deleted file mode 100644
index 9f4cb19463..0000000000
--- a/src/python/graplctl/graplctl/upload/lib.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import annotations
-
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-import click
-from grapl_tests_common.upload_analyzers import AnalyzerUpload, upload_analyzers
-
-if TYPE_CHECKING:
-    from mypy_boto3_s3 import S3Client
-
-
-def upload_analyzer(
-    s3_client: S3Client,
-    analyzers_bucket: str,
-    analyzer_main_py: Path,
-) -> None:
-    # The assumption is that the input path is something like
-    # /home/grapl/etc/local_grapl/unique_cmd_parent/main.py
-    # where the 'unique_cmd_parent' is the name of the analyzer
-    name = analyzer_main_py.parent.name
-    s3_key = f"analyzers/{name}/main.py"
-    upload_request = AnalyzerUpload(local_path=str(analyzer_main_py), s3_key=s3_key)
-    upload_analyzers(
-        s3_client=s3_client,
-        analyzers=(upload_request,),
-        analyzers_bucket=analyzers_bucket,
-    )
-    click.echo(f"Uploaded analyzer '{name}'")
diff --git a/src/python/python-proto/python_proto/api.py b/src/python/python-proto/python_proto/api.py
index b771fbc3e2..dabe9b86a4 100644
--- a/src/python/python-proto/python_proto/api.py
+++ b/src/python/python-proto/python_proto/api.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 import dataclasses
-from typing import Mapping, Sequence, Type, Union, cast
+from typing import Mapping, Optional, Sequence, Type, Union, cast
 
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import (
     DecrementOnlyIntProp as _DecrementOnlyIntProp,
@@ -11,6 +11,7 @@
 )
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import Edge as _Edge
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import EdgeList as _EdgeList
+from graplinc.grapl.api.graph.v1beta1.types_pb2 import ExecutionHit as _ExecutionHit
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import (
     GraphDescription as _GraphDescription,
 )
@@ -34,6 +35,7 @@
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import (
     IncrementOnlyUintProp as _IncrementOnlyUintProp,
 )
+from graplinc.grapl.api.graph.v1beta1.types_pb2 import Lens as _Lens
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import MergedEdge as _MergedEdge
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import MergedEdgeList as _MergedEdgeList
 from graplinc.grapl.api.graph.v1beta1.types_pb2 import MergedGraph as _MergedGraph
@@ -732,3 +734,85 @@ def into_proto(self) -> _MergedGraph:
         for k2, v2 in self.edges.items():
             proto_merged_graph.edges[k2].CopyFrom(v2.into_proto())
         return proto_merged_graph
+
+
+@dataclasses.dataclass(frozen=True)
+class Lens(SerDe[_Lens]):
+    lens_type: str
+    lens_name: str
+    uid: Optional[int] = None
+    score: Optional[int] = None
+    proto_cls: Type[_Lens] = _Lens
+
+    @staticmethod
+    def deserialize(bytes_: bytes) -> Lens:
+        proto_lens = _Lens()
+        proto_lens.ParseFromString(bytes_)
+        return Lens.from_proto(proto_lens=proto_lens)
+
+    @staticmethod
+    def from_proto(proto_lens: _Lens) -> Lens:
+        return Lens(
+            lens_type=proto_lens.lens_type,
+            lens_name=proto_lens.lens_name,
+            uid=proto_lens.uid,
+            score=proto_lens.score,
+        )
+
+    def into_proto(self) -> _Lens:
+        proto_lens = _Lens()
+        proto_lens.lens_type = self.lens_type
+        proto_lens.lens_name = self.lens_name
+        if self.uid is not None:
+            proto_lens.uid = self.uid
+        if self.score is not None:
+            proto_lens.score = self.score
+        return proto_lens
+
+
+@dataclasses.dataclass(frozen=True)
+class ExecutionHit(SerDe[_ExecutionHit]):
+    nodes: Mapping[str, MergedNode]
+    edges: Mapping[str, MergedEdgeList]
+    analyzer_name: str
+    risk_score: int
+    lenses: Sequence[Lens]
+    risky_node_keys: Sequence[str]
+    proto_cls: Type[_ExecutionHit] = _ExecutionHit
+
+    @staticmethod
+    def deserialize(bytes_: bytes) -> ExecutionHit:
+        proto_execution_hit = _ExecutionHit()
+        proto_execution_hit.ParseFromString(bytes_)
+        return ExecutionHit.from_proto(proto_execution_hit=proto_execution_hit)
+
+    @staticmethod
+    def from_proto(proto_execution_hit: _ExecutionHit) -> ExecutionHit:
+        return ExecutionHit(
+            nodes={
+                k: MergedNode.from_proto(v)
+                for k, v in proto_execution_hit.nodes.items()
+            },
+            edges={
+                k: MergedEdgeList.from_proto(v)
+                for k, v in proto_execution_hit.edges.items()
+            },
+            analyzer_name=proto_execution_hit.analyzer_name,
+            risk_score=proto_execution_hit.risk_score,
+            lenses=[Lens.from_proto(l) for l in proto_execution_hit.lenses],
+            risky_node_keys=proto_execution_hit.risky_node_keys,
+        )
+
+    def into_proto(self) -> _ExecutionHit:
+        proto_execution_hit = _ExecutionHit()
+        for k1, v1 in self.nodes.items():
+            proto_execution_hit.nodes[k1].CopyFrom(v1.into_proto())
+        for k2, v2 in self.edges.items():
+            proto_execution_hit.edges[k2].CopyFrom(v2.into_proto())
+        proto_execution_hit.analyzer_name = self.analyzer_name
+        proto_execution_hit.risk_score = self.risk_score
+        for lens in self.lenses:
+            proto_execution_hit.lenses.append(lens.into_proto())
+        for risky_node_key in self.risky_node_keys:
+            proto_execution_hit.risky_node_keys.append(risky_node_key)
+        return proto_execution_hit
diff --git a/src/python/python-proto/python_proto/pipeline.py b/src/python/python-proto/python_proto/pipeline.py
index 43e8ec8e45..74d2afc10b 100644
--- a/src/python/python-proto/python_proto/pipeline.py
+++ b/src/python/python-proto/python_proto/pipeline.py
@@ -6,7 +6,6 @@
 from typing import Generic, Type, cast
 
 from google.protobuf.any_pb2 import Any as _Any
-from graplinc.grapl.pipeline.v1beta1.types_pb2 import Envelope as _OldEnvelope
 from graplinc.grapl.pipeline.v1beta1.types_pb2 import Metadata as _Metadata
 from graplinc.grapl.pipeline.v1beta1.types_pb2 import RawLog as _RawLog
 from graplinc.grapl.pipeline.v1beta2.types_pb2 import NewEnvelope as _Envelope
@@ -93,34 +92,6 @@ def into_proto(self) -> _Envelope:
         return proto_envelope
 
 
-@dataclasses.dataclass(frozen=True)
-class OldEnvelope(SerDe[_OldEnvelope]):
-    metadata: Metadata
-    inner_type: str
-    inner_message: bytes
-
-    @staticmethod
-    def deserialize(bytes_: bytes) -> OldEnvelope:
-        proto_envelope = _OldEnvelope()
-        proto_envelope.ParseFromString(bytes_)
-        return OldEnvelope.from_proto(proto_envelope=proto_envelope)
-
-    @staticmethod
-    def from_proto(proto_envelope: _OldEnvelope) -> OldEnvelope:
-        return OldEnvelope(
-            metadata=Metadata.from_proto(proto_envelope.metadata),
-            inner_type="fixme",
-            inner_message=proto_envelope.inner_message,
-        )
-
-    def into_proto(self) -> _OldEnvelope:
-        proto_envelope = _OldEnvelope()
-        proto_envelope.metadata.CopyFrom(self.metadata.into_proto())
-        proto_envelope.inner_message = self.inner_message
-        proto_envelope.inner_type = self.inner_type
-        return proto_envelope
-
-
 @dataclasses.dataclass(frozen=True)
 class RawLog(SerDe[_RawLog]):
     log_event: bytes
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index d288080a82..01e92089e8 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -267,12 +267,6 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
-[[package]]
-name = "adler32"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
-
 [[package]]
 name = "aead"
 version = "0.4.3"
@@ -343,35 +337,6 @@ dependencies = [
  "alloc-no-stdlib",
 ]
 
-[[package]]
-name = "analyzer-dispatcher"
-version = "1.0.0"
-dependencies = [
- "async-trait",
- "base64",
- "bytes",
- "chrono",
- "failure",
- "futures",
- "grapl-config",
- "grapl-observe",
- "grapl-service",
- "log",
- "prost 0.9.0",
- "rusoto_core",
- "rusoto_credential",
- "rusoto_s3",
- "rusoto_sqs",
- "rust-proto",
- "serde",
- "serde_json",
- "sqs-executor",
- "thiserror",
- "tokio",
- "uuid",
- "zstd",
-]
-
 [[package]]
 name = "ansi_term"
 version = "0.12.1"
@@ -387,12 +352,6 @@ version = "1.0.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
 
-[[package]]
-name = "arc-swap"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
-
 [[package]]
 name = "argon2"
 version = "0.4.0"
@@ -905,20 +864,6 @@ dependencies = [
  "unreachable",
 ]
 
-[[package]]
-name = "combine"
-version = "4.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948"
-dependencies = [
- "bytes",
- "futures-core",
- "memchr",
- "pin-project-lite",
- "tokio",
- "tokio-util 0.7.3",
-]
-
 [[package]]
 name = "concurrent-queue"
 version = "1.2.2"
@@ -1237,7 +1182,7 @@ dependencies = [
  "log",
  "proc-macro2",
  "quote",
- "rust-proto-new",
+ "rust-proto",
  "serde",
  "serde_derive",
  "serde_json",
@@ -1400,12 +1345,6 @@ version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
 
-[[package]]
-name = "dtoa"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
-
 [[package]]
 name = "duct"
 version = "0.13.5"
@@ -1441,7 +1380,7 @@ name = "endpoint-plugin"
 version = "0.1.0"
 dependencies = [
  "derive-dynamic-node",
- "rust-proto-new",
+ "rust-proto",
  "thiserror",
  "uuid",
 ]
@@ -1491,13 +1430,12 @@ dependencies = [
  "clap 3.1.18",
  "grapl-config",
  "grapl-utils",
- "rust-proto-new",
+ "rust-proto",
  "sqlx",
  "test-log",
  "thiserror",
  "tokio",
  "tracing",
- "tracing-futures",
  "tracing-subscriber",
  "uuid",
 ]
@@ -1720,7 +1658,7 @@ dependencies = [
  "opentelemetry 0.16.0",
  "opentelemetry-jaeger",
  "plugin-work-queue",
- "rust-proto-new",
+ "rust-proto",
  "test-context",
  "tokio",
  "tracing",
@@ -1738,7 +1676,7 @@ dependencies = [
  "clap 3.1.18",
  "consul-connect",
  "moka",
- "rust-proto-new",
+ "rust-proto",
  "serde",
  "thiserror",
  "tokio",
@@ -1800,31 +1738,6 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
-[[package]]
-name = "graph-generator-lib"
-version = "0.1.9"
-dependencies = [
- "base64",
- "chrono",
- "grapl-config",
- "grapl-observe",
- "grapl-service",
- "log",
- "prost 0.9.0",
- "rusoto_core",
- "rusoto_credential",
- "rusoto_s3",
- "rusoto_sqs",
- "rust-proto",
- "serde",
- "serde_json",
- "sha2 0.9.9",
- "sqs-executor",
- "tokio",
- "tracing",
- "zstd",
-]
-
 [[package]]
 name = "graph-merger"
 version = "1.0.0"
@@ -1847,7 +1760,7 @@ dependencies = [
  "rusoto_core",
  "rusoto_credential",
  "rusoto_dynamodb",
- "rust-proto-new",
+ "rust-proto",
  "serde_json",
  "test-context",
  "thiserror",
@@ -1865,20 +1778,10 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474"
 dependencies = [
- "combine 3.8.1",
+ "combine",
  "thiserror",
 ]
 
-[[package]]
-name = "grapl"
-version = "0.3.1"
-dependencies = [
- "derive-dynamic-node",
- "graph-generator-lib",
- "grapl-config",
- "rust-proto",
-]
-
 [[package]]
 name = "grapl-config"
 version = "0.0.2"
@@ -1896,11 +1799,9 @@ dependencies = [
  "rusoto_dynamodb",
  "rusoto_s3",
  "rusoto_sqs",
- "sqs-executor",
  "tokio",
  "tracing",
  "tracing-appender",
- "tracing-futures",
  "tracing-opentelemetry 0.16.0",
  "tracing-subscriber",
 ]
@@ -1931,24 +1832,6 @@ dependencies = [
  "thiserror",
 ]
 
-[[package]]
-name = "grapl-service"
-version = "0.1.0"
-dependencies = [
- "bytes",
- "grapl-config",
- "itertools 0.10.3",
- "libflate",
- "prost 0.9.0",
- "rust-proto",
- "serde",
- "serde_json",
- "sqs-executor",
- "thiserror",
- "tracing",
- "zstd",
-]
-
 [[package]]
 name = "grapl-utils"
 version = "0.1.0"
@@ -2341,7 +2224,7 @@ dependencies = [
  "clap 3.1.18",
  "futures",
  "rdkafka",
- "rust-proto-new",
+ "rust-proto",
  "thiserror",
  "tracing",
 ]
@@ -2373,26 +2256,6 @@ version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
 
-[[package]]
-name = "libflate"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05605ab2bce11bcfc0e9c635ff29ef8b2ea83f29be257ee7d730cac3ee373093"
-dependencies = [
- "adler32",
- "crc32fast",
- "libflate_lz77",
-]
-
-[[package]]
-name = "libflate_lz77"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39a734c0493409afcd49deee13c006a04e3586b9761a03543c6272c9c51f2f5a"
-dependencies = [
- "rle-decode-fast",
-]
-
 [[package]]
 name = "libz-sys"
 version = "1.1.8"
@@ -2638,7 +2501,6 @@ dependencies = [
  "tonic-health 0.5.0",
  "tower",
  "tracing",
- "tracing-futures",
  "tracing-subscriber",
 ]
 
@@ -2695,7 +2557,7 @@ dependencies = [
  "rusoto_core",
  "rusoto_credential",
  "rusoto_dynamodb",
- "rust-proto-new",
+ "rust-proto",
  "serde",
  "serde_dynamodb",
  "sha2 0.9.9",
@@ -2948,19 +2810,16 @@ name = "organization-management"
 version = "0.1.0"
 dependencies = [
  "argon2",
+ "async-trait",
  "clap 3.1.18",
  "grapl-config",
  "grapl-utils",
- "prost 0.9.0",
  "rust-proto",
  "sqlx",
  "test-log",
  "thiserror",
  "tokio",
- "tonic 0.6.2",
- "tonic-build 0.6.2",
  "tracing",
- "tracing-futures",
  "tracing-subscriber",
  "uuid",
 ]
@@ -3117,7 +2976,7 @@ dependencies = [
  "kafka",
  "opentelemetry 0.16.0",
  "opentelemetry-jaeger",
- "rust-proto-new",
+ "rust-proto",
  "test-context",
  "thiserror",
  "tokio",
@@ -3174,8 +3033,6 @@ dependencies = [
  "rust-proto",
  "thiserror",
  "tokio",
- "tonic 0.6.2",
- "tonic-health 0.5.0",
  "tracing",
  "uuid",
 ]
@@ -3189,7 +3046,7 @@ dependencies = [
  "generator-sdk",
  "grapl-config",
  "plugin-work-queue",
- "rust-proto-new",
+ "rust-proto",
  "tokio",
  "tracing",
 ]
@@ -3208,7 +3065,7 @@ dependencies = [
  "nomad-client-gen",
  "rusoto_core",
  "rusoto_s3",
- "rust-proto-new",
+ "rust-proto",
  "serde_json",
  "sqlx",
  "tempfile",
@@ -3231,7 +3088,7 @@ dependencies = [
  "futures",
  "grapl-config",
  "grapl-utils",
- "rust-proto-new",
+ "rust-proto",
  "sqlx",
  "thiserror",
  "tokio",
@@ -3669,28 +3526,6 @@ dependencies = [
  "zstd-sys",
 ]
 
-[[package]]
-name = "redis"
-version = "0.21.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a80b5f38d7f5a020856a0e16e40a9cfabf88ae8f0e4c2dcd8a3114c1e470852"
-dependencies = [
- "arc-swap",
- "async-trait",
- "bytes",
- "combine 4.6.4",
- "dtoa",
- "futures",
- "futures-util",
- "itoa 0.4.8",
- "percent-encoding",
- "pin-project-lite",
- "sha1",
- "tokio",
- "tokio-util 0.6.10",
- "url",
-]
-
 [[package]]
 name = "redox_syscall"
 version = "0.2.13"
@@ -3810,12 +3645,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "rle-decode-fast"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
-
 [[package]]
 name = "rusoto_cloudwatch"
 version = "0.47.0"
@@ -3942,26 +3771,6 @@ dependencies = [
 
 [[package]]
 name = "rust-proto"
-version = "0.2.10"
-dependencies = [
- "log",
- "prost 0.9.0",
- "prost-types 0.9.0",
- "quickcheck",
- "quickcheck_macros",
- "rand 0.8.5",
- "serde",
- "serde_derive",
- "thiserror",
- "tonic 0.6.2",
- "tonic-build 0.6.2",
- "tracing",
- "tracing-subscriber",
- "uuid",
-]
-
-[[package]]
-name = "rust-proto-new"
 version = "0.1.0"
 dependencies = [
  "bytes",
@@ -4246,21 +4055,6 @@ dependencies = [
  "digest 0.10.3",
 ]
 
-[[package]]
-name = "sha1"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
-dependencies = [
- "sha1_smol",
-]
-
-[[package]]
-name = "sha1_smol"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
-
 [[package]]
 name = "sha2"
 version = "0.9.9"
@@ -4481,36 +4275,6 @@ dependencies = [
  "tokio-rustls 0.22.0",
 ]
 
-[[package]]
-name = "sqs-executor"
-version = "0.1.0"
-dependencies = [
- "async-trait",
- "chrono",
- "futures",
- "futures-util",
- "grapl-observe",
- "grapl-utils",
- "hex",
- "itertools 0.10.3",
- "lazy_static",
- "lru",
- "num_cpus",
- "prost 0.9.0",
- "redis",
- "rusoto_core",
- "rusoto_s3",
- "rusoto_sqs",
- "rust-proto",
- "serde",
- "serde_json",
- "thiserror",
- "tokio",
- "tracing",
- "tracing-futures",
- "uuid",
-]
-
 [[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
@@ -4581,7 +4345,7 @@ dependencies = [
  "kafka",
  "opentelemetry 0.16.0",
  "opentelemetry-jaeger",
- "rust-proto-new",
+ "rust-proto",
  "sysmon-parser",
  "test-context",
  "thiserror",
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 9370b7bf69..6b7c076f38 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -1,19 +1,15 @@
 [workspace]
 members = [
-  "./analyzer-dispatcher",
   "./derive-dynamic-node",
   "./consul-connect",
   "./endpoint-plugin",
   "./event-source",
   "./generator-dispatcher",
-  "./generators/graph-generator-lib",
   "./generators/sysmon-generator",
   "./graph-merger",
-  "./grapl",
   "./grapl-config",
   "./grapl-graphql-codegen",
   "./grapl-observe",
-  "./grapl-service",
   "./grapl-web-ui",
   "./grapl-utils",
   "./kafka",
@@ -28,8 +24,6 @@ members = [
   "./plugin-sdk/generator-sdk/",
   "./plugin-work-queue",
   "./rust-proto",
-  "./rust-proto-new",
-  "./sqs-executor",
   "./sysmon-parser",
   "./uid-allocator",
 ]
diff --git a/src/rust/Dockerfile b/src/rust/Dockerfile
index 0633b17ce4..d92af0f4c5 100644
--- a/src/rust/Dockerfile
+++ b/src/rust/Dockerfile
@@ -23,7 +23,7 @@ RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=rust-base-apt
     && apt-get install --yes --no-install-recommends \
         build-essential=12.9 \
         cmake=3.18.4-2+deb11u1 \
-        libssl-dev=1.1.1n-0+deb11u2 \
+        libssl-dev=1.1.1n-0+deb11u3 \
         perl=5.32.1-4+deb11u2 \
         pkg-config=0.29.2-1 \
         tcl=8.6.11+1
@@ -108,7 +108,7 @@ FROM base AS tarpaulin
 RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=rust-tarpaulin-apt \
     apt-get update \
     && apt-get install --yes --no-install-recommends \
-        libssl-dev=1.1.1n-0+deb11u2 \
+        libssl-dev=1.1.1n-0+deb11u3 \
         pkg-config=0.29.2-1
 
 # For test coverage reports
@@ -137,72 +137,7 @@ FROM base AS build-test-integration
 # https://github.com/rust-lang/cargo/issues/1924
 # https://github.com/rust-lang/cargo/issues/3670
 
-ENV RUST_INTEGRATION_TEST_FEATURES="sqs-executor/integration,model-plugin-deployer/integration,plugin-work-queue/integration,organization-management/integration"
-ENV TEST_DIR=/grapl/tests
-
-RUN mkdir --parents "${TEST_DIR}"
-
-# This will build the integration test binaries and parse the manifest to find
-# their paths for copying later.
-#
-# Hadolint is confused again, at the time of this writing, SHELL *does*
-# have -o pipefail set on line 9.
-# hadolint ignore=DL4006
-RUN --mount=type=cache,target="${CARGO_TARGET_DIR}",sharing=locked \
-    --mount=type=cache,target="${REGISTRY_CACHE_TARGET}",sharing=locked \
-    --mount=type=cache,target="${RUSTUP_CACHE_TARGET}",sharing=locked \
-    cargo test \
-        --features "${RUST_INTEGRATION_TEST_FEATURES}" \
-        --no-run \
-        --message-format=json \
-        --test "*" | \
-        jq -r "select(.profile.test == true) | .filenames[]" | \
-        xargs \
-          --replace="{}" \
-          cp "{}" "${TEST_DIR}/"
-
-
-# integration tests distribution
-################################################################################
-# We're unable to use one of the 'distroless' container images as a base here
-# because our integration tests require zlib shared library, but we don't have
-# a way of including that in the base image. With a debian image we can apt
-# install as needed, but the debian image we're using has zlib already.
-FROM debian:bullseye-slim AS integration-tests
-
-RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=rust-tests-apt \
-    apt-get update \
-    && apt-get install --yes --no-install-recommends \
-        ca-certificates=20210119
-
-# Put the Nomad binary on PATH
-# so that it's available to integration test consumers of NomadCli
-COPY --from=build /nomad/nomad /bin
-
-COPY --from=build-test-integration /grapl/tests /tests
-# Named context support https://github.com/hadolint/hadolint/issues/830
-# hadolint ignore=DL3022
-COPY --from=test-ctx ./run/rust-integration-tests.sh /
-ENTRYPOINT [ "/rust-integration-tests.sh" ]
-
-
-# build-test-integration-new
-################################################################################
-FROM base AS build-test-integration-new
-
-# For running integration tests we're going to copy the test binaries to a new
-# container base and run the directly, as opposed to running them via `cargo
-# test`. Cargo will recompile tests if it thinks the test binaries in target/
-# are out of date. Because we're using a mount cache when building the sources
-# this directly won't be available in resulting container images. In the past
-# we've `cp -a` the target directory to preserve it, but this can make for an
-# increasingly large container image size, especially when the mount cache is
-# has not been cleaned in a while. To find the test binaries paths we parse
-# the manifest.json from the cargo build.
-# https://github.com/rust-lang/cargo/issues/1924
-# https://github.com/rust-lang/cargo/issues/3670
-
-ENV RUST_INTEGRATION_TEST_FEATURES="generator-dispatcher/new_integration_tests,graph-merger/new_integration_tests,node-identifier/new_integration_tests,pipeline-ingress/new_integration_tests,plugin-registry/new_integration_tests,sysmon-generator/new_integration_tests,event-source/new_integration_tests"
+ENV RUST_INTEGRATION_TEST_FEATURES="generator-dispatcher/integration_tests,graph-merger/integration_tests,model-plugin-deployer/integration_tests,node-identifier/integration_tests,pipeline-ingress/integration_tests,plugin-registry/integration_tests,sysmon-generator/integration_tests,organization-management/integration_tests"
 ENV TEST_DIR=/grapl/tests
 
 # This will build the integration test binaries and parse the manifest to find
@@ -221,19 +156,19 @@ RUN --mount=type=cache,target="${CARGO_TARGET_DIR}",sharing=locked \
         --no-run \
         --message-format=json \
         --test "*" | \
-        jq -r "select((.profile.test == true) and (.features[] | contains(\"new_integration_tests\"))) | .filenames[]" | \
+        jq -r "select((.profile.test == true) and (.features[] | contains(\"integration_tests\"))) | .filenames[]" | \
         xargs \
           --replace="{}" \
           cp "{}" "${TEST_DIR}/"
 
 
-# "new" integration tests distribution
+# integration tests distribution
 ################################################################################
 # We're unable to use one of the 'distroless' container images as a base here
 # because our integration tests require zlib shared library, but we don't have
 # a way of including that in the base image. With a debian image we can apt
 # install as needed, but the debian image we're using has zlib already.
-FROM debian:bullseye-slim AS integration-tests-new
+FROM debian:bullseye-slim AS integration-tests
 
 RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=rust-tests-apt \
     apt-get update \
@@ -248,7 +183,7 @@ COPY --from=build /nomad/nomad /bin
 RUN mkdir -p /test-fixtures
 COPY --from=build /outputs/example-generator /test-fixtures
 
-COPY --from=build-test-integration-new /grapl/tests /tests
+COPY --from=build-test-integration /grapl/tests /tests
 # Named context support https://github.com/hadolint/hadolint/issues/830
 # hadolint ignore=DL3022
 COPY --from=test-ctx ./run/rust-integration-tests.sh /
@@ -281,12 +216,6 @@ COPY --from=build /outputs/example-generator /
 COPY rust/plugin-bootstrap/grapl-plugin-bootstrap-init.service /plugin-bootstrap-init/
 COPY rust/plugin-bootstrap/grapl-plugin.service /plugin-bootstrap-init/
 
-##### analyzer-dispatcher
-FROM rust-dist AS analyzer-dispatcher-deploy
-
-COPY --from=build /outputs/analyzer-dispatcher /
-ENTRYPOINT ["/analyzer-dispatcher"]
-
 ##### graph-merger
 FROM rust-dist AS graph-merger-deploy
 
@@ -373,4 +302,4 @@ ENTRYPOINT ["/generator-dispatcher"]
 ##### event-source
 FROM rust-dist as event-source-deploy
 COPY --from=build /outputs/event-source /
-ENTRYPOINT ["/event-source"]
\ No newline at end of file
+ENTRYPOINT ["/event-source"]
diff --git a/src/rust/analyzer-dispatcher/Cargo.toml b/src/rust/analyzer-dispatcher/Cargo.toml
deleted file mode 100644
index e34dcd453f..0000000000
--- a/src/rust/analyzer-dispatcher/Cargo.toml
+++ /dev/null
@@ -1,42 +0,0 @@
-[package]
-name = "analyzer-dispatcher"
-version = "1.0.0"
-authors = ["Insanitybit <insanitybit@gmail.com>"]
-edition = "2021"
-
-[dependencies]
-rust-proto = { path = "../rust-proto", version = "*" }
-grapl-config = { path = "../grapl-config", version = "*" }
-grapl-observe = { path = "../grapl-observe" }
-grapl-service = { path = "../grapl-service" }
-futures = "0.3"
-base64 = "0.13.0"
-bytes = "1.1.0"
-failure = "0.1.8"
-log = "0.4.14"
-prost = "0.9.0"
-rusoto_s3 = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-rusoto_core = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-rusoto_sqs = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-rusoto_credential = "0.47.0"
-async-trait = "0.1.51"
-zstd = "0.10.0"
-tokio = { version = "1.17", features = [
-  "macros",
-  "rt",
-  "rt-multi-thread",
-  "sync",
-  "time",
-] }
-chrono = "0.4.19"
-sqs-executor = { path = "../sqs-executor/" }
-thiserror = "1.0.30"
-serde = { version = "1.0.130", features = ["derive"] }
-serde_json = "1.0.72"
-uuid = { version = "0.8", features = ["v4"] }
diff --git a/src/rust/analyzer-dispatcher/src/dispatch_event.rs b/src/rust/analyzer-dispatcher/src/dispatch_event.rs
deleted file mode 100644
index 7ebf16f78f..0000000000
--- a/src/rust/analyzer-dispatcher/src/dispatch_event.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-use prost::Message;
-use rust_proto::{
-    graph_descriptions::*,
-    pipeline::ServiceMessage,
-};
-use serde::{
-    Deserialize,
-    Serialize,
-};
-use serde_json::json;
-use sqs_executor::completion_event_serializer::CompletionEventSerializer;
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct AnalyzerDispatchEvent {
-    key: String,
-    subgraph: MergedGraph,
-}
-
-impl AnalyzerDispatchEvent {
-    pub fn new(key: String, subgraph: MergedGraph) -> Self {
-        Self { key, subgraph }
-    }
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct AnalyzerDispatchEvents {
-    events: Vec<AnalyzerDispatchEvent>,
-}
-
-impl AnalyzerDispatchEvents {
-    pub fn new() -> Self {
-        Self { events: vec![] }
-    }
-}
-
-impl Default for AnalyzerDispatchEvents {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl From<Vec<AnalyzerDispatchEvent>> for AnalyzerDispatchEvents {
-    fn from(events: Vec<AnalyzerDispatchEvent>) -> Self {
-        Self { events }
-    }
-}
-
-impl ServiceMessage for AnalyzerDispatchEvents {
-    const TYPE_NAME: &'static str = "AnalyzerDispatchEvent";
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum DispatchEventEncoderError {
-    #[error("IO")]
-    Io(#[from] std::io::Error),
-    #[error("EncodeError")]
-    JsonEncodeError(#[from] serde_json::Error),
-    #[error("ProtoEncodeError")]
-    ProtoEncodeError(#[from] prost::EncodeError),
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct AnalyzerDispatchSerializer {}
-
-impl CompletionEventSerializer for AnalyzerDispatchSerializer {
-    type CompletedEvent = AnalyzerDispatchEvents;
-    type Output = Vec<u8>;
-    type Error = DispatchEventEncoderError;
-
-    fn serialize_completed_events(
-        &mut self,
-        completed_events: &[Self::CompletedEvent],
-    ) -> Result<Vec<Self::Output>, Self::Error> {
-        let unique_events: Vec<_> = completed_events.iter().flat_map(|e| &e.events).collect();
-
-        let mut final_subgraph = MergedGraph::new();
-
-        for event in unique_events.iter() {
-            final_subgraph.merge(&event.subgraph);
-        }
-
-        let mut serialized = Vec::with_capacity(unique_events.len());
-
-        let mut buf = Vec::with_capacity(5000);
-        final_subgraph.encode(&mut buf)?;
-
-        for event in unique_events {
-            let event = json!({
-                "key": event.key,
-                "subgraph": buf,
-            });
-
-            serialized.push(serde_json::to_vec(&event)?);
-        }
-
-        Ok(serialized)
-    }
-}
diff --git a/src/rust/analyzer-dispatcher/src/main.rs b/src/rust/analyzer-dispatcher/src/main.rs
deleted file mode 100644
index 416c230ec8..0000000000
--- a/src/rust/analyzer-dispatcher/src/main.rs
+++ /dev/null
@@ -1,230 +0,0 @@
-use std::{
-    sync::Arc,
-    time::Duration,
-};
-
-use async_trait::async_trait;
-use failure::{
-    bail,
-    Error,
-};
-use grapl_config::env_helpers::{
-    s3_event_emitters_from_env,
-    FromEnv,
-};
-use grapl_observe::metric_reporter::MetricReporter;
-use grapl_service::decoder::ProtoDecoder;
-use log::{
-    debug,
-    error,
-    info,
-    warn,
-};
-use rusoto_s3::{
-    ListObjectsRequest,
-    S3Client,
-    S3,
-};
-use rusoto_sqs::SqsClient;
-use rust_proto::graph_descriptions::*;
-use sqs_executor::{
-    cache::NopCache,
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-    event_handler::{
-        CompletedEvents,
-        EventHandler,
-    },
-    make_ten,
-    s3_event_retriever::S3PayloadRetriever,
-    time_based_key_fn,
-};
-
-use crate::dispatch_event::{
-    AnalyzerDispatchEvent,
-    AnalyzerDispatchEvents,
-    AnalyzerDispatchSerializer,
-};
-
-pub mod dispatch_event;
-
-#[derive(Debug)]
-pub struct AnalyzerDispatcher<S>
-where
-    S: S3 + Send + Sync + 'static,
-{
-    s3_client: Arc<S>,
-}
-
-impl<S> Clone for AnalyzerDispatcher<S>
-where
-    S: S3 + Send + Sync + 'static,
-{
-    fn clone(&self) -> Self {
-        Self {
-            s3_client: self.s3_client.clone(),
-        }
-    }
-}
-
-async fn get_s3_keys(
-    s3_client: &impl S3,
-    bucket: impl Into<String>,
-) -> Result<impl IntoIterator<Item = Result<String, Error>>, Error> {
-    let bucket = bucket.into();
-
-    let list_res = tokio::time::timeout(
-        Duration::from_secs(2),
-        s3_client.list_objects(ListObjectsRequest {
-            bucket,
-            ..Default::default()
-        }),
-    )
-    .await??;
-
-    let contents = match list_res.contents {
-        Some(contents) => contents,
-        None => {
-            warn!("List response returned nothing");
-            Vec::new()
-        }
-    };
-
-    Ok(contents.into_iter().map(|object| match object.key {
-        Some(key) => Ok(key),
-        None => bail!("S3Object is missing key"),
-    }))
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum AnalyzerDispatcherError {
-    #[error("Unexpected")]
-    Unexpected(String),
-}
-
-impl CheckedError for AnalyzerDispatcherError {
-    fn error_type(&self) -> Recoverable {
-        Recoverable::Transient
-    }
-}
-
-#[async_trait]
-impl<S> EventHandler for AnalyzerDispatcher<S>
-where
-    S: S3 + Send + Sync + 'static,
-{
-    type InputEvent = MergedGraph;
-    type OutputEvent = AnalyzerDispatchEvents;
-    type Error = AnalyzerDispatcherError;
-
-    async fn handle_event(
-        &mut self,
-        subgraph: Self::InputEvent,
-        _completed: &mut CompletedEvents,
-    ) -> Result<Self::OutputEvent, Result<(Self::OutputEvent, Self::Error), Self::Error>> {
-        let bucket = std::env::var("GRAPL_ANALYZERS_BUCKET").expect("GRAPL_ANALYZERS_BUCKET");
-
-        if subgraph.is_empty() {
-            warn!("Attempted to handle empty subgraph");
-            return Ok(AnalyzerDispatchEvents::new());
-        }
-
-        info!("Retrieving S3 keys");
-        let keys = match get_s3_keys(self.s3_client.as_ref(), &bucket).await {
-            Ok(keys) => keys,
-            Err(e) => {
-                return Err(Err(AnalyzerDispatcherError::Unexpected(format!(
-                    "Failed to list bucket: {} with {:?}",
-                    bucket, e
-                ))));
-            }
-        };
-
-        let mut dispatch_events = Vec::new();
-
-        let mut failed = None;
-        for key in keys {
-            let key = match key {
-                Ok(key) => key,
-                Err(e) => {
-                    warn!("Failed to retrieve key with {:?}", e);
-                    failed = Some(e);
-                    continue;
-                }
-            };
-
-            dispatch_events.push(AnalyzerDispatchEvent::new(key, subgraph.clone()));
-        }
-
-        if let Some(e) = failed {
-            Err(Ok((
-                AnalyzerDispatchEvents::from(dispatch_events),
-                AnalyzerDispatcherError::Unexpected(e.to_string()),
-            )))
-        } else {
-            Ok(AnalyzerDispatchEvents::from(dispatch_events))
-        }
-    }
-}
-
-async fn handler() -> Result<(), Box<dyn std::error::Error>> {
-    let (env, _guard) = grapl_config::init_grapl_env!();
-
-    info!("Handling event");
-
-    let sqs_client = SqsClient::from_env();
-    let _s3_client = S3Client::from_env();
-    let source_queue_url = grapl_config::source_queue_url();
-    let dead_letter_queue_url = grapl_config::dead_letter_queue_url();
-    debug!("Queue Url: {}", source_queue_url);
-    debug!("Dead-Letter Queue Url: {}", dead_letter_queue_url);
-
-    let cache = &mut make_ten(async {
-        NopCache {} // the AnalyzerDispatcher is not idempotent :(
-    })
-    .await;
-
-    let serializer = &mut make_ten(async { AnalyzerDispatchSerializer::default() }).await;
-
-    let s3_emitter = &mut s3_event_emitters_from_env(&env, time_based_key_fn).await;
-
-    let s3_payload_retriever = &mut make_ten(async {
-        S3PayloadRetriever::new(
-            |region_str| grapl_config::env_helpers::init_s3_client(&region_str),
-            ProtoDecoder::default(),
-            MetricReporter::new(&env.service_name),
-        )
-    })
-    .await;
-    let analyzer_dispatcher = &mut make_ten(async {
-        AnalyzerDispatcher {
-            s3_client: Arc::new(S3Client::from_env()),
-        }
-    })
-    .await;
-
-    info!("Starting process_loop");
-    sqs_executor::process_loop(
-        source_queue_url,
-        dead_letter_queue_url,
-        cache,
-        sqs_client.clone(),
-        analyzer_dispatcher,
-        s3_payload_retriever,
-        s3_emitter,
-        serializer,
-        MetricReporter::new(&env.service_name),
-    )
-    .await;
-
-    info!("Exiting");
-    Ok(())
-}
-
-#[tokio::main]
-async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    handler().await?;
-    Ok(())
-}
diff --git a/src/rust/build-env.Dockerfile b/src/rust/build-env.Dockerfile
index 7c6ef7b53c..80d4f6b958 100644
--- a/src/rust/build-env.Dockerfile
+++ b/src/rust/build-env.Dockerfile
@@ -15,7 +15,7 @@ RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=rust-build-en
     apt-get install --yes --no-install-recommends \
         build-essential=12.9 \
         cmake=3.18.4-2+deb11u1 \
-        libssl-dev=1.1.1n-0+deb11u2 \
+        libssl-dev=1.1.1n-0+deb11u3 \
         perl=5.32.1-4+deb11u2 \
         pkg-config=0.29.2-1 \
         tcl=8.6.11+1 \
diff --git a/src/rust/derive-dynamic-node/Cargo.toml b/src/rust/derive-dynamic-node/Cargo.toml
index 7a4679cefb..70d998e5be 100644
--- a/src/rust/derive-dynamic-node/Cargo.toml
+++ b/src/rust/derive-dynamic-node/Cargo.toml
@@ -16,7 +16,7 @@ quote = "1.0.10"
 proc-macro2 = "1.0.32"
 
 [dev-dependencies]
-rust-proto-new = { path = "../rust-proto-new", version = "*" }
+rust-proto = { path = "../rust-proto", version = "*" }
 serde = "1.0.130"
 serde_json = "1.0.72"
 serde_derive = "1.0.130"
diff --git a/src/rust/derive-dynamic-node/examples/ex.rs b/src/rust/derive-dynamic-node/examples/ex.rs
index 8e08b0fb7f..2ec4a7f2ec 100644
--- a/src/rust/derive-dynamic-node/examples/ex.rs
+++ b/src/rust/derive-dynamic-node/examples/ex.rs
@@ -5,7 +5,7 @@ use derive_dynamic_node::{
     NodeDescription,
 };
 use log::info;
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     GraphDescription,
     IdStrategy,
     ImmutableStrProp,
diff --git a/src/rust/derive-dynamic-node/src/lib.rs b/src/rust/derive-dynamic-node/src/lib.rs
index a6ef02667f..a303e279f8 100644
--- a/src/rust/derive-dynamic-node/src/lib.rs
+++ b/src/rust/derive-dynamic-node/src/lib.rs
@@ -97,21 +97,21 @@ pub fn derive_node_description(input: TokenStream) -> TokenStream {
 
         #[derive(Clone, Debug)]
         pub struct #node_name {
-            dynamic_node: rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription,
+            dynamic_node: rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription,
         }
 
         pub trait #node_trait_name {
-            fn get_mut_dynamic_node(&mut self) -> &mut rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription;
-            fn get_dynamic_node(&self) -> &rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription;
+            fn get_mut_dynamic_node(&mut self) -> &mut rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription;
+            fn get_dynamic_node(&self) -> &rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription;
 
             #methods
         }
 
         impl #node_name {
-            pub fn new(strategy: rust_proto_new::graplinc::grapl::api::graph::v1beta1::IdStrategy) -> Self {
+            pub fn new(strategy: rust_proto::graplinc::grapl::api::graph::v1beta1::IdStrategy) -> Self {
                 let mut properties = std::collections::HashMap::with_capacity(1);
 
-                let dynamic_node = rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription {
+                let dynamic_node = rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription {
                     node_type: #struct_name_string .to_owned(),
                     id_strategy: vec![strategy],
                     node_key: uuid::Uuid::new_v4().to_string(),
@@ -134,20 +134,20 @@ pub fn derive_node_description(input: TokenStream) -> TokenStream {
             }
         }
 
-        impl AsRef<rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription> for #node_name {
+        impl AsRef<rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription> for #node_name {
             fn as_ref(&self) -> &NodeDescription {
                 &self.dynamic_node
             }
         }
 
-        impl AsMut<rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription> for #node_name {
+        impl AsMut<rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription> for #node_name {
             fn as_mut(&mut self) -> &mut NodeDescription {
                 &mut self.dynamic_node
             }
         }
 
-        impl From<#node_name> for rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription {
-            fn from(n: #node_name) -> rust_proto_new::graplinc::grapl::api::graph::v1beta1::NodeDescription {
+        impl From<#node_name> for rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription {
+            fn from(n: #node_name) -> rust_proto::graplinc::grapl::api::graph::v1beta1::NodeDescription {
                 n.dynamic_node
             }
         }
@@ -350,49 +350,49 @@ fn resolvable_type_from(
                 /* underlying struct field type    maps to this type   via this method on NodeProperty */
                 ("String", IMMUTABLE) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::ImmutableStrProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::ImmutableStrProp
                     ),
                     parse_quote!(as_immutable_str),
                 ),
                 ("std::string::String", IMMUTABLE) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::ImmutableStrProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::ImmutableStrProp
                     ),
                     parse_quote!(as_immutable_str),
                 ),
                 ("u64", IMMUTABLE) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::ImmutableUintProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::ImmutableUintProp
                     ),
                     parse_quote!(as_immutable_uint),
                 ),
                 ("u64", INCREMENT) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::IncrementOnlyUintProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::IncrementOnlyUintProp
                     ),
                     parse_quote!(as_increment_only_uint),
                 ),
                 ("u64", DECREMENT) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::DecrementOnlyUintProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::DecrementOnlyUintProp
                     ),
                     parse_quote!(as_decrement_only_uint),
                 ),
                 ("i64", IMMUTABLE) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::ImmutableIntProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::ImmutableIntProp
                     ),
                     parse_quote!(as_immutable_int),
                 ),
                 ("i64", INCREMENT) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::IncrementOnlyIntProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::IncrementOnlyIntProp
                     ),
                     parse_quote!(as_increment_only_int),
                 ),
                 ("i64", DECREMENT) => (
                     parse_quote!(
-                        rust_proto_new::graplinc::grapl::api::graph::v1beta1::DecrementOnlyIntProp
+                        rust_proto::graplinc::grapl::api::graph::v1beta1::DecrementOnlyIntProp
                     ),
                     parse_quote!(as_decrement_only_int),
                 ),
@@ -441,8 +441,8 @@ fn identity_prop_setter(field: &Field, property_name: &Ident) -> TS2 {
     quote!(
         let mut self_strategy = &mut mut_self.id_strategy[0].strategy;
         match self_strategy {
-            rust_proto_new::graplinc::grapl::api::graph::v1beta1::Strategy::Session(
-                rust_proto_new::graplinc::grapl::api::graph::v1beta1::Session{ref mut #ident, ..}
+            rust_proto::graplinc::grapl::api::graph::v1beta1::Strategy::Session(
+                rust_proto::graplinc::grapl::api::graph::v1beta1::Session{ref mut #ident, ..}
             ) => {
                 * #ident = #property_name.as_inner().clone();
             }
diff --git a/src/rust/endpoint-plugin/Cargo.toml b/src/rust/endpoint-plugin/Cargo.toml
index 2b12e0548f..3d6c4956a6 100644
--- a/src/rust/endpoint-plugin/Cargo.toml
+++ b/src/rust/endpoint-plugin/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
-rust-proto-new = { path = "../rust-proto-new", version = "*" }
+rust-proto = { path = "../rust-proto", version = "*" }
 derive-dynamic-node = { path = "../derive-dynamic-node" }
 uuid = { version = "0.8", features = ["v4"] }
 thiserror = "1.0.30"
diff --git a/src/rust/endpoint-plugin/src/asset.rs b/src/rust/endpoint-plugin/src/asset.rs
index 203969123a..1d58cffd3e 100644
--- a/src/rust/endpoint-plugin/src/asset.rs
+++ b/src/rust/endpoint-plugin/src/asset.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplStaticId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/file.rs b/src/rust/endpoint-plugin/src/file.rs
index 924a550f40..20bd11d4b4 100644
--- a/src/rust/endpoint-plugin/src/file.rs
+++ b/src/rust/endpoint-plugin/src/file.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplSessionId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/ip_address.rs b/src/rust/endpoint-plugin/src/ip_address.rs
index f1c80d06df..1e676515bd 100644
--- a/src/rust/endpoint-plugin/src/ip_address.rs
+++ b/src/rust/endpoint-plugin/src/ip_address.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplStaticId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/ip_connection.rs b/src/rust/endpoint-plugin/src/ip_connection.rs
index e652eb36e8..5894758a02 100644
--- a/src/rust/endpoint-plugin/src/ip_connection.rs
+++ b/src/rust/endpoint-plugin/src/ip_connection.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplSessionId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/ip_port.rs b/src/rust/endpoint-plugin/src/ip_port.rs
index dedf8fdd90..ba1bdfd19e 100644
--- a/src/rust/endpoint-plugin/src/ip_port.rs
+++ b/src/rust/endpoint-plugin/src/ip_port.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplStaticId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/network_connection.rs b/src/rust/endpoint-plugin/src/network_connection.rs
index 19aae2238a..81bb39eb36 100644
--- a/src/rust/endpoint-plugin/src/network_connection.rs
+++ b/src/rust/endpoint-plugin/src/network_connection.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplSessionId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/process.rs b/src/rust/endpoint-plugin/src/process.rs
index 68600f3826..fc074b2aa9 100644
--- a/src/rust/endpoint-plugin/src/process.rs
+++ b/src/rust/endpoint-plugin/src/process.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplSessionId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/process_inbound_connection.rs b/src/rust/endpoint-plugin/src/process_inbound_connection.rs
index 10a9d44c33..1e6c4478a3 100644
--- a/src/rust/endpoint-plugin/src/process_inbound_connection.rs
+++ b/src/rust/endpoint-plugin/src/process_inbound_connection.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplSessionId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/endpoint-plugin/src/process_outbound_connection.rs b/src/rust/endpoint-plugin/src/process_outbound_connection.rs
index 259caf6e57..b7ab5a66e0 100644
--- a/src/rust/endpoint-plugin/src/process_outbound_connection.rs
+++ b/src/rust/endpoint-plugin/src/process_outbound_connection.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplSessionId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     NodeDescription,
     NodeProperty,
diff --git a/src/rust/event-source/Cargo.toml b/src/rust/event-source/Cargo.toml
index 268dbafdb5..b53490a073 100644
--- a/src/rust/event-source/Cargo.toml
+++ b/src/rust/event-source/Cargo.toml
@@ -17,13 +17,12 @@ sqlx = { version = "0.5", features = [
   "runtime-tokio-rustls",
   "uuid",
 ] }
-tracing-futures = "0.2.5"
 tracing = "0.1.29"
 tracing-subscriber = { version = "0.3", default-features = false, features = [
   "env-filter",
   "fmt"
 ] }
-rust-proto-new = { path = "../rust-proto-new" }
+rust-proto = { path = "../rust-proto" }
 thiserror = "1.0.26"
 clap = { version = "3.0", default_features = false, features = [
   "std",
@@ -37,4 +36,4 @@ clap = { version = "3.0", default_features = false, features = [
 test-log = { version = "0.2", default-features = false, features = ["trace"] }
 
 [features]
-new_integration_tests = []
+integration_tests = []
diff --git a/src/rust/event-source/src/client.rs b/src/rust/event-source/src/client.rs
index 55294db6e3..1a7b5b0a01 100644
--- a/src/rust/event-source/src/client.rs
+++ b/src/rust/event-source/src/client.rs
@@ -1,7 +1,7 @@
 use std::time::Duration;
 
 use async_trait;
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::event_source::v1beta1::client::{
         EventSourceServiceClient,
         EventSourceServiceClientError,
diff --git a/src/rust/event-source/src/error.rs b/src/rust/event-source/src/error.rs
index 4b16018a8c..052fa232fb 100644
--- a/src/rust/event-source/src/error.rs
+++ b/src/rust/event-source/src/error.rs
@@ -1,4 +1,4 @@
-use rust_proto_new::protocol::status::Status;
+use rust_proto::protocol::status::Status;
 
 use crate::db::EventSourceDbError;
 
diff --git a/src/rust/event-source/src/server.rs b/src/rust/event-source/src/server.rs
index 862d13e581..0c03d576eb 100644
--- a/src/rust/event-source/src/server.rs
+++ b/src/rust/event-source/src/server.rs
@@ -1,6 +1,6 @@
 use std::time::Duration;
 
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::event_source::{
         v1beta1 as native,
         v1beta1::server::{
diff --git a/src/rust/generator-dispatcher/Cargo.toml b/src/rust/generator-dispatcher/Cargo.toml
index b0aefa16db..060b34d04e 100644
--- a/src/rust/generator-dispatcher/Cargo.toml
+++ b/src/rust/generator-dispatcher/Cargo.toml
@@ -18,7 +18,7 @@ futures = "0.3"
 grapl-config = { path = "../grapl-config" }
 kafka = { path = "../kafka", version = "*" }
 plugin-work-queue = { path = "../plugin-work-queue" }
-rust-proto-new = { path = "../rust-proto-new" }
+rust-proto = { path = "../rust-proto" }
 tokio = { version = "1.17", features = ["macros", "rt", "rt-multi-thread"] }
 tracing = "0.1.29"
 uuid = { version = "0.8", features = ["v4"] }
@@ -32,7 +32,7 @@ opentelemetry-jaeger = { version = "0.15", features = [
   "rt-tokio"
 ] }
 plugin-work-queue = { path = "../plugin-work-queue", features = [
-  "new_integration_tests"
+  "integration_tests"
 ] }
 test-context = "0.1"
 tracing-appender = "0.2"
@@ -40,4 +40,4 @@ tracing-opentelemetry = "0.16"
 tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
 
 [features]
-new_integration_tests = []
+integration_tests = []
diff --git a/src/rust/generator-dispatcher/src/main.rs b/src/rust/generator-dispatcher/src/main.rs
index 328e149df0..730cf16d2f 100644
--- a/src/rust/generator-dispatcher/src/main.rs
+++ b/src/rust/generator-dispatcher/src/main.rs
@@ -6,7 +6,7 @@ use plugin_work_queue::client::{
     FromEnv,
     PluginWorkQueueServiceClient,
 };
-use rust_proto_new::graplinc::grapl::{
+use rust_proto::graplinc::grapl::{
     api::plugin_work_queue::v1beta1::{
         ExecutionJob,
         PutExecuteGeneratorRequest,
diff --git a/src/rust/generators/graph-generator-lib/Cargo.toml b/src/rust/generators/graph-generator-lib/Cargo.toml
deleted file mode 100644
index 73cc4df66c..0000000000
--- a/src/rust/generators/graph-generator-lib/Cargo.toml
+++ /dev/null
@@ -1,34 +0,0 @@
-[package]
-name = "graph-generator-lib"
-version = "0.1.9"
-authors = ["Insanitybit <insanitybit@gmail.com>"]
-edition = "2021"
-description = "Library for writing Grapl generator services"
-license = "Apache-2.0"
-
-[dependencies]
-rust-proto = { path = "../../rust-proto", version = "0.2.*" }
-sqs-executor = { path = "../../sqs-executor" }
-grapl-service = { path = "../../grapl-service" }
-grapl-observe = { path = "../../grapl-observe" }
-sha2 = "0.9.8"
-rusoto_s3 = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-rusoto_core = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-rusoto_sqs = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-grapl-config = { path = "../../grapl-config", version = "0.0.*" }
-tracing = "0.1.29"
-rusoto_credential = "0.47.0"
-base64 = "0.13.0"
-prost = "0.9.0"
-serde = "1.0.130"
-serde_json = "1.0.72"
-log = "0.4.14"
-zstd = "0.10.0"
-chrono = "0.4.19"
-tokio = "1.17"
diff --git a/src/rust/generators/graph-generator-lib/src/lib.rs b/src/rust/generators/graph-generator-lib/src/lib.rs
deleted file mode 100755
index c576f21ce5..0000000000
--- a/src/rust/generators/graph-generator-lib/src/lib.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-#![allow(unused_must_use)]
-
-use std::fmt::Debug;
-
-pub use grapl_config;
-use grapl_config::{
-    event_caches,
-    ServiceEnv,
-};
-pub use grapl_observe::metric_reporter::MetricReporter;
-use grapl_service::serialization::GraphDescriptionSerializer;
-use rusoto_s3::S3Client;
-use rusoto_sqs::SqsClient;
-pub use rust_proto::graph_descriptions::*;
-use sqs_executor::{
-    errors::CheckedError,
-    event_decoder::PayloadDecoder,
-    event_handler::EventHandler,
-    make_ten,
-    redis_cache::RedisCache,
-    s3_event_retriever::S3PayloadRetriever,
-    time_based_key_fn,
-};
-use tracing::info;
-
-use crate::grapl_config::env_helpers::{
-    s3_event_emitters_from_env,
-    FromEnv,
-};
-
-pub async fn run_graph_generator<
-    InputEventT,
-    HandlerErrorT,
-    InitGenerator,
-    PayloadDecoderT,
-    DecoderErrorT,
-    EventHandlerT,
->(
-    env: ServiceEnv,
-    init_generator: InitGenerator,
-    payload_decoder: PayloadDecoderT,
-) where
-    InputEventT: Send + 'static,
-    InitGenerator: Clone + Send + 'static + Fn(RedisCache) -> EventHandlerT,
-    EventHandlerT: EventHandler<
-            InputEvent = InputEventT,
-            OutputEvent = GraphDescription,
-            Error = HandlerErrorT,
-        >
-        + Send
-        + Sync
-        + 'static
-        + Clone,
-    HandlerErrorT: Debug + CheckedError + Send + Sync + 'static,
-    PayloadDecoderT:
-        PayloadDecoder<InputEventT, DecoderError = DecoderErrorT> + Send + Sync + Clone + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-{
-    let sqs_client = SqsClient::from_env();
-    let _s3_client = S3Client::from_env();
-    let cache = &mut event_caches(&env).await;
-
-    let subgraph_generator = &mut make_ten(async { (init_generator)(cache[0].clone()) }).await;
-
-    let serializer = &mut make_ten(async { GraphDescriptionSerializer::default() }).await;
-
-    let s3_emitter = &mut s3_event_emitters_from_env(&env, time_based_key_fn).await;
-
-    let s3_payload_retriever = &mut make_ten(async {
-        S3PayloadRetriever::new(
-            |region_str| {
-                info!("Initializing new s3 client: {}", &region_str);
-                grapl_config::env_helpers::init_s3_client(&region_str)
-            },
-            payload_decoder,
-            MetricReporter::new(&env.service_name),
-        )
-    })
-    .await;
-
-    info!("Starting process_loop");
-    sqs_executor::process_loop(
-        grapl_config::source_queue_url(),
-        grapl_config::dead_letter_queue_url(),
-        cache,
-        sqs_client.clone(),
-        subgraph_generator,
-        s3_payload_retriever,
-        s3_emitter,
-        serializer,
-        MetricReporter::new(&env.service_name),
-    )
-    .await;
-}
diff --git a/src/rust/generators/sysmon-generator/Cargo.toml b/src/rust/generators/sysmon-generator/Cargo.toml
index b7959212c1..a4f96c689e 100644
--- a/src/rust/generators/sysmon-generator/Cargo.toml
+++ b/src/rust/generators/sysmon-generator/Cargo.toml
@@ -19,7 +19,7 @@ opentelemetry-jaeger = { version = "0.15", features = [
   "collector_client",
   "rt-tokio"
 ] }
-rust-proto-new = { path = "../../rust-proto-new", version = "*" }
+rust-proto = { path = "../../rust-proto", version = "*" }
 sysmon-parser = { path = "../../sysmon-parser/" }
 thiserror = "1.0.30"
 tokio = { version = "1.17", features = [
@@ -42,4 +42,4 @@ bytes = "1.0"
 test-context = "0.1"
 
 [features]
-new_integration_tests = []
+integration_tests = []
diff --git a/src/rust/generators/sysmon-generator/src/main.rs b/src/rust/generators/sysmon-generator/src/main.rs
index 6c2529ade0..0cfbf165a6 100644
--- a/src/rust/generators/sysmon-generator/src/main.rs
+++ b/src/rust/generators/sysmon-generator/src/main.rs
@@ -12,7 +12,7 @@ use opentelemetry::{
     global,
     sdk::propagation::TraceContextPropagator,
 };
-use rust_proto_new::graplinc::grapl::{
+use rust_proto::graplinc::grapl::{
     api::graph::v1beta1::GraphDescription,
     pipeline::{
         v1beta1::{
diff --git a/src/rust/generators/sysmon-generator/src/models.rs b/src/rust/generators/sysmon-generator/src/models.rs
index e7b67734b3..1b5f7d99c6 100644
--- a/src/rust/generators/sysmon-generator/src/models.rs
+++ b/src/rust/generators/sysmon-generator/src/models.rs
@@ -2,7 +2,7 @@ use chrono::{
     DateTime,
     Utc,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::GraphDescription;
+use rust_proto::graplinc::grapl::api::graph::v1beta1::GraphDescription;
 use sysmon_parser::{
     EventData,
     SysmonEvent,
diff --git a/src/rust/generators/sysmon-generator/src/models/file/create.rs b/src/rust/generators/sysmon-generator/src/models/file/create.rs
index 7ec5b2388a..b9c3d2e63a 100644
--- a/src/rust/generators/sysmon-generator/src/models/file/create.rs
+++ b/src/rust/generators/sysmon-generator/src/models/file/create.rs
@@ -6,7 +6,7 @@ use endpoint_plugin::{
     IProcessNode,
     ProcessNode,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::GraphDescription;
+use rust_proto::graplinc::grapl::api::graph::v1beta1::GraphDescription;
 use sysmon_parser::{
     event_data::FileCreateEventData,
     System,
diff --git a/src/rust/generators/sysmon-generator/src/models/network/outbound.rs b/src/rust/generators/sysmon-generator/src/models/network/outbound.rs
index 80af98af82..93392428e6 100644
--- a/src/rust/generators/sysmon-generator/src/models/network/outbound.rs
+++ b/src/rust/generators/sysmon-generator/src/models/network/outbound.rs
@@ -14,7 +14,7 @@ use endpoint_plugin::{
     ProcessNode,
     ProcessOutboundConnectionNode,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::GraphDescription;
+use rust_proto::graplinc::grapl::api::graph::v1beta1::GraphDescription;
 use sysmon_parser::{
     event_data::NetworkConnectionEventData,
     System,
diff --git a/src/rust/generators/sysmon-generator/src/models/process/create.rs b/src/rust/generators/sysmon-generator/src/models/process/create.rs
index a82672f4ce..9d5a91fa4b 100644
--- a/src/rust/generators/sysmon-generator/src/models/process/create.rs
+++ b/src/rust/generators/sysmon-generator/src/models/process/create.rs
@@ -6,7 +6,7 @@ use endpoint_plugin::{
     IProcessNode,
     ProcessNode,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::GraphDescription;
+use rust_proto::graplinc::grapl::api::graph::v1beta1::GraphDescription;
 use sysmon_parser::{
     event_data::ProcessCreateEventData,
     System,
@@ -100,7 +100,7 @@ pub(crate) fn generate_process_create_subgraph(
 }
 #[cfg(test)]
 mod tests {
-    use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+    use rust_proto::graplinc::grapl::api::graph::v1beta1::{
         GraphDescription,
         ImmutableUintProp,
         NodeDescription,
diff --git a/src/rust/graph-merger/Cargo.toml b/src/rust/graph-merger/Cargo.toml
index a62b67ba93..dbc6453dee 100644
--- a/src/rust/graph-merger/Cargo.toml
+++ b/src/rust/graph-merger/Cargo.toml
@@ -13,7 +13,7 @@ name = "graph-merger"
 path = "src/main.rs"
 
 [features]
-new_integration_tests = []
+integration_tests = []
 
 [dependencies]
 anyhow = "1.0"
@@ -41,7 +41,7 @@ rusoto_dynamodb = { version = "0.47.0", default_features = false, features = [
   "rustls"
 ] }
 rusoto_credential = "0.47.0"
-rust-proto-new = { path = "../rust-proto-new", version = "*" }
+rust-proto = { path = "../rust-proto", version = "*" }
 serde_json = "1.0"
 thiserror = "1.0"
 tokio = { version = "1.17", features = [
diff --git a/src/rust/graph-merger/src/main.rs b/src/rust/graph-merger/src/main.rs
index b10e81cf24..3e43a76adb 100644
--- a/src/rust/graph-merger/src/main.rs
+++ b/src/rust/graph-merger/src/main.rs
@@ -17,7 +17,7 @@ use opentelemetry::{
     sdk::propagation::TraceContextPropagator,
 };
 use rusoto_dynamodb::DynamoDbClient;
-use rust_proto_new::graplinc::grapl::{
+use rust_proto::graplinc::grapl::{
     api::graph::v1beta1::{
         IdentifiedGraph,
         MergedGraph,
diff --git a/src/rust/graph-merger/src/reverse_resolver.rs b/src/rust/graph-merger/src/reverse_resolver.rs
index 185d0f6424..50605636c1 100644
--- a/src/rust/graph-merger/src/reverse_resolver.rs
+++ b/src/rust/graph-merger/src/reverse_resolver.rs
@@ -11,7 +11,7 @@ use rusoto_dynamodb::{
     DynamoDbClient,
     KeysAndAttributes,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::Edge;
+use rust_proto::graplinc::grapl::api::graph::v1beta1::Edge;
 
 use crate::service::GraphMergerError;
 
diff --git a/src/rust/graph-merger/src/service.rs b/src/rust/graph-merger/src/service.rs
index 825a78d0a7..a71e2819f2 100644
--- a/src/rust/graph-merger/src/service.rs
+++ b/src/rust/graph-merger/src/service.rs
@@ -8,7 +8,7 @@ use std::{
 };
 
 use dgraph_tonic::Client as DgraphClient;
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdentifiedGraph,
     MergedGraph,
 };
diff --git a/src/rust/graph-merger/src/upsert_util.rs b/src/rust/graph-merger/src/upsert_util.rs
index 9dc3a45473..a5c035bed8 100644
--- a/src/rust/graph-merger/src/upsert_util.rs
+++ b/src/rust/graph-merger/src/upsert_util.rs
@@ -1,6 +1,6 @@
 use std::collections::HashMap;
 
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     NodeProperty,
     Property,
 };
diff --git a/src/rust/graph-merger/src/upserter.rs b/src/rust/graph-merger/src/upserter.rs
index 2672aec4f1..367ce73c99 100644
--- a/src/rust/graph-merger/src/upserter.rs
+++ b/src/rust/graph-merger/src/upserter.rs
@@ -37,7 +37,7 @@ use futures_retry::{
     RetryPolicy,
 };
 use grapl_utils::iter_ext::GraplIterExt;
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     Edge,
     EdgeList,
     IdentifiedGraph,
diff --git a/src/rust/grapl-config/Cargo.toml b/src/rust/grapl-config/Cargo.toml
index 97e1a17208..6570019a89 100644
--- a/src/rust/grapl-config/Cargo.toml
+++ b/src/rust/grapl-config/Cargo.toml
@@ -9,7 +9,6 @@ license = "Apache-2.0"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
 log = "0.4.14"
-sqs-executor = { path = "../sqs-executor/" }
 grapl-observe = { path = "../grapl-observe/" }
 opentelemetry = { version = "0.16.0", features = ["rt-tokio"] }
 opentelemetry-jaeger = { version = "0.15.0", features = [
@@ -35,7 +34,6 @@ rusoto_credential = { version = "0.47.0", default_features = false }
 tracing = "0.1.29"
 tracing-opentelemetry = "0.16.0"
 tracing-subscriber = { version = "0.3.2", features = ["env-filter", "json"] }
-tracing-futures = "0.2.5"
 tokio = { version = "1.17", features = [
   "macros",
   "rt",
diff --git a/src/rust/grapl-config/src/env_helpers.rs b/src/rust/grapl-config/src/env_helpers.rs
index e5fff4b83b..4fabf4a7f2 100644
--- a/src/rust/grapl-config/src/env_helpers.rs
+++ b/src/rust/grapl-config/src/env_helpers.rs
@@ -12,10 +12,6 @@ use rusoto_core::{
 use rusoto_dynamodb::DynamoDbClient;
 use rusoto_s3::S3Client;
 use rusoto_sqs::SqsClient;
-use sqs_executor::{
-    make_ten,
-    s3_event_emitter::S3EventEmitter,
-};
 
 use crate::ServiceEnv;
 
@@ -239,25 +235,3 @@ impl From<&ServiceEnv> for MetricReporter<Stdout> {
         MetricReporter::new(&env.service_name)
     }
 }
-
-pub fn s3_event_emitter_from_env<F>(env: &ServiceEnv, key_fn: F) -> S3EventEmitter<S3Client, F>
-where
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-{
-    S3EventEmitter::new(
-        S3Client::from_env(),
-        crate::dest_bucket(),
-        key_fn,
-        MetricReporter::new(&env.service_name),
-    )
-}
-
-pub async fn s3_event_emitters_from_env<F>(
-    env: &ServiceEnv,
-    key_fn: F,
-) -> [S3EventEmitter<S3Client, F>; 10]
-where
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-{
-    make_ten(async { s3_event_emitter_from_env(env, key_fn) }).await
-}
diff --git a/src/rust/grapl-config/src/lib.rs b/src/rust/grapl-config/src/lib.rs
index 55a788b8f3..7fab505234 100644
--- a/src/rust/grapl-config/src/lib.rs
+++ b/src/rust/grapl-config/src/lib.rs
@@ -1,11 +1,9 @@
 use std::{
-    io::Stdout,
     str::FromStr,
     time::Duration,
 };
 
 use color_eyre::Help;
-use grapl_observe::metric_reporter::MetricReporter;
 use opentelemetry::{
     global,
     sdk::propagation::TraceContextPropagator,
@@ -18,10 +16,6 @@ use rusoto_sqs::{
     ListQueuesRequest,
     Sqs,
 };
-use sqs_executor::{
-    make_ten,
-    redis_cache::RedisCache,
-};
 use tracing::debug;
 use tracing_subscriber::{
     prelude::*,
@@ -56,31 +50,6 @@ pub fn _init_grapl_env(
     (env, tracing_guard)
 }
 
-pub async fn event_cache(env: &ServiceEnv) -> RedisCache {
-    let cache_address = std::env::var("REDIS_ENDPOINT").expect("REDIS_ENDPOINT");
-    if !cache_address.starts_with("redis://") {
-        panic!(
-            "Expected redis client with redis://, but got {}",
-            cache_address
-        );
-    }
-    let lru_cache_size = std::env::var("LRU_CACHE_SIZE")
-        .unwrap_or(String::from("1000000"))
-        .parse::<usize>()
-        .unwrap_or(1_000_000);
-    RedisCache::with_lru_capacity(
-        lru_cache_size,
-        cache_address.to_owned(),
-        MetricReporter::<Stdout>::new(&env.service_name),
-    )
-    .await
-    .expect("Could not create redis client")
-}
-
-pub async fn event_caches(env: &ServiceEnv) -> [RedisCache; 10] {
-    make_ten(event_cache(env)).await
-}
-
 pub fn dest_bucket() -> String {
     std::env::var("DEST_BUCKET_NAME").expect("DEST_BUCKET_NAME")
 }
diff --git a/src/rust/grapl-service/Cargo.toml b/src/rust/grapl-service/Cargo.toml
deleted file mode 100644
index 94860704a0..0000000000
--- a/src/rust/grapl-service/Cargo.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[package]
-name = "grapl-service"
-version = "0.1.0"
-authors = ["colin <colin@graplsecurity.com>"]
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-[dependencies]
-sqs-executor = { path = "../sqs-executor" }
-rust-proto = { path = "../rust-proto" }
-grapl-config = { path = "../grapl-config" }
-zstd = "0.10.0"
-libflate = "1.1.1"
-prost = "0.9.0"
-tracing = "0.1.29"
-thiserror = "1.0.30"
-serde = "1.0.130"
-serde_json = "1.0.72"
-bytes = "1.1.0"
-itertools = "0.10.1"
diff --git a/src/rust/grapl-service/src/decoder/decompress.rs b/src/rust/grapl-service/src/decoder/decompress.rs
deleted file mode 100644
index 15d2bb61d9..0000000000
--- a/src/rust/grapl-service/src/decoder/decompress.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-use std::{
-    io::{
-        Cursor,
-        Read,
-    },
-    str::FromStr,
-    sync::Arc,
-};
-
-use libflate::gzip::Decoder as GzDecoder;
-use sqs_executor::errors::{
-    CheckedError,
-    Recoverable,
-};
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum PayloadDecompression {
-    Gzip,
-    None,
-    Zstd,
-}
-
-impl FromStr for PayloadDecompression {
-    type Err = ();
-
-    fn from_str(input: &str) -> Result<PayloadDecompression, Self::Err> {
-        match input.to_lowercase().as_str() {
-            "gzip" => Ok(PayloadDecompression::Gzip),
-            "none" => Ok(PayloadDecompression::None),
-            "zstd" => Ok(PayloadDecompression::Zstd),
-            _ => Err(()),
-        }
-    }
-}
-
-#[derive(thiserror::Error, Clone, Debug)]
-pub enum PayloadDecompressionError {
-    #[error("DecompressionError")]
-    DecompressionError(Arc<std::io::Error>),
-}
-
-impl From<std::io::Error> for PayloadDecompressionError {
-    fn from(err: std::io::Error) -> Self {
-        PayloadDecompressionError::DecompressionError(Arc::new(err))
-    }
-}
-
-impl CheckedError for PayloadDecompressionError {
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::DecompressionError(_) => Recoverable::Persistent,
-        }
-    }
-}
-
-pub fn maybe_decompress(input: &[u8]) -> Result<Vec<u8>, PayloadDecompressionError> {
-    let value = grapl_config::source_compression();
-    match PayloadDecompression::from_str(value.as_str()).expect("PayloadDecompression") {
-        PayloadDecompression::Gzip => {
-            let mut decoder = GzDecoder::new(input)?;
-            let mut decoded_data = Vec::with_capacity(input.len());
-
-            decoder.read_to_end(&mut decoded_data)?;
-
-            Ok(decoded_data)
-        }
-        PayloadDecompression::None => Ok(input.to_vec()),
-        PayloadDecompression::Zstd => {
-            let body = Cursor::new(input);
-
-            zstd::stream::decode_all(body).map_err(|e| e.into())
-        }
-    }
-}
diff --git a/src/rust/grapl-service/src/decoder/json.rs b/src/rust/grapl-service/src/decoder/json.rs
deleted file mode 100644
index f934342683..0000000000
--- a/src/rust/grapl-service/src/decoder/json.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use serde::Deserialize;
-use sqs_executor::{
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-    event_decoder::PayloadDecoder,
-};
-
-use crate::decoder::decompress::PayloadDecompressionError;
-
-#[derive(thiserror::Error, Debug)]
-pub enum JsonDecoderError {
-    #[error("DecompressionError {0}")]
-    DecompressionError(#[from] PayloadDecompressionError),
-    #[error("JsonError {0}")]
-    JsonError(#[from] serde_json::Error),
-}
-
-impl CheckedError for JsonDecoderError {
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::DecompressionError(_) => Recoverable::Persistent,
-            Self::JsonError(_) => Recoverable::Persistent,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Default)]
-pub struct JsonDecoder;
-
-impl<D> PayloadDecoder<D> for JsonDecoder
-where
-    for<'a> D: Deserialize<'a>,
-{
-    type DecoderError = JsonDecoderError;
-    fn decode(&mut self, body: Vec<u8>) -> Result<D, Self::DecoderError> {
-        let decompressed = super::decompress::maybe_decompress(body.as_slice())?;
-
-        serde_json::from_slice(&decompressed).map_err(|e| e.into())
-    }
-}
diff --git a/src/rust/grapl-service/src/decoder/mod.rs b/src/rust/grapl-service/src/decoder/mod.rs
deleted file mode 100644
index cce175df77..0000000000
--- a/src/rust/grapl-service/src/decoder/mod.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-pub mod decompress;
-pub mod json;
-pub mod ndjson;
-pub mod proto;
-
-pub use json::{
-    JsonDecoder,
-    JsonDecoderError,
-};
-pub use ndjson::NdjsonDecoder;
-pub use proto::{
-    ProtoDecoder,
-    ProtoDecoderError,
-};
diff --git a/src/rust/grapl-service/src/decoder/ndjson.rs b/src/rust/grapl-service/src/decoder/ndjson.rs
deleted file mode 100644
index bd3d3f83a6..0000000000
--- a/src/rust/grapl-service/src/decoder/ndjson.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use serde::Deserialize;
-use sqs_executor::{
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-    event_decoder::PayloadDecoder,
-};
-
-use crate::decoder::decompress::PayloadDecompressionError;
-
-#[derive(Debug, Clone, Default)]
-pub struct NdjsonDecoder;
-
-#[derive(thiserror::Error, Debug)]
-pub enum NdjsonDecoderError {
-    #[error("DecompressionError {0}")]
-    Decompression(#[from] PayloadDecompressionError),
-    #[error("DeserializeError {0}")]
-    Deserialization(#[from] serde_json::Error),
-}
-
-impl CheckedError for NdjsonDecoderError {
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::Decompression(_) => Recoverable::Persistent,
-            Self::Deserialization(_) => Recoverable::Persistent,
-        }
-    }
-}
-
-impl<D> PayloadDecoder<Vec<D>> for NdjsonDecoder
-where
-    for<'a> D: Deserialize<'a>,
-{
-    type DecoderError = NdjsonDecoderError;
-
-    fn decode(&mut self, body: Vec<u8>) -> Result<Vec<D>, Self::DecoderError> {
-        let decompressed = super::decompress::maybe_decompress(body.as_slice())?;
-
-        let mut first_deserialization_error: Option<serde_json::Error> = None;
-
-        let events: Vec<_> = decompressed
-            .split(|byte| *byte == b'\n')
-            .filter(|line| !line.is_empty())
-            .filter_map(|line| {
-                let result = serde_json::from_slice(line);
-                match result {
-                    Ok(result) => Some(result),
-                    Err(error) => {
-                        tracing::error!(message="Unable to deserialize OSQuery event.", error=?error);
-
-                        if first_deserialization_error.is_none() {
-                            first_deserialization_error = Some(error);
-                        }
-                        None
-                    }
-                }
-            })
-            .collect();
-
-        // This is a bit awkward at the moment, due to interfaces to the sqs-executor.  If some of
-        // our events successfully parsed then we want to continue and send those to the event
-        // handler. Only if all parsing has failed and we have no events do we want to return an
-        // error here. Note the other error condition is a failure to decompress above.
-        match first_deserialization_error {
-            Some(error) if events.is_empty() => Err(error.into()),
-            _ => Ok(events),
-        }
-    }
-}
diff --git a/src/rust/grapl-service/src/decoder/proto.rs b/src/rust/grapl-service/src/decoder/proto.rs
deleted file mode 100644
index f295f1f7eb..0000000000
--- a/src/rust/grapl-service/src/decoder/proto.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use prost::{
-    DecodeError,
-    Message,
-};
-use sqs_executor::{
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-    event_decoder::PayloadDecoder,
-};
-
-use crate::decoder::decompress::PayloadDecompressionError;
-
-#[derive(thiserror::Error, Debug)]
-pub enum ProtoDecoderError {
-    #[error("DecompressionError {0}")]
-    DecompressionError(#[from] PayloadDecompressionError),
-    #[error("ProtoError {0}")]
-    ProtoError(#[from] DecodeError),
-}
-
-impl CheckedError for ProtoDecoderError {
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::DecompressionError(_) => Recoverable::Persistent,
-            Self::ProtoError(_) => Recoverable::Persistent,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Default)]
-pub struct ProtoDecoder;
-
-impl<E> PayloadDecoder<E> for ProtoDecoder
-where
-    E: Message + Default,
-{
-    type DecoderError = ProtoDecoderError;
-    fn decode(&mut self, body: Vec<u8>) -> Result<E, Self::DecoderError>
-    where
-        E: Message + Default,
-    {
-        let decompressed = super::decompress::maybe_decompress(body.as_slice())?;
-
-        let buf = prost::bytes::Bytes::from(decompressed);
-        E::decode(buf).map_err(|e| e.into())
-    }
-}
diff --git a/src/rust/grapl-service/src/lib.rs b/src/rust/grapl-service/src/lib.rs
deleted file mode 100644
index 577de35ebb..0000000000
--- a/src/rust/grapl-service/src/lib.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-pub mod decoder;
-pub mod serialization;
-
-#[cfg(test)]
-mod tests {
-    #[test]
-    fn it_works() {
-        assert_eq!(2 + 2, 4);
-    }
-}
diff --git a/src/rust/grapl-service/src/serialization/mod.rs b/src/rust/grapl-service/src/serialization/mod.rs
deleted file mode 100644
index 5bb58ebb31..0000000000
--- a/src/rust/grapl-service/src/serialization/mod.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-pub mod zstd_proto_graph;
-
-pub use zstd_proto_graph::{
-    GraphDescriptionSerializer,
-    GraphDescriptionSerializerError,
-    IdentifiedGraphSerializer,
-    IdentifiedGraphSerializerError,
-    MergedGraphSerializer,
-    MergedGraphSerializerError,
-};
diff --git a/src/rust/grapl-service/src/serialization/zstd_proto_graph.rs b/src/rust/grapl-service/src/serialization/zstd_proto_graph.rs
deleted file mode 100644
index 07018f92bc..0000000000
--- a/src/rust/grapl-service/src/serialization/zstd_proto_graph.rs
+++ /dev/null
@@ -1,252 +0,0 @@
-use prost::EncodeError;
-use rust_proto::graph_descriptions::*;
-use sqs_executor::completion_event_serializer::CompletionEventSerializer;
-use tracing::{
-    debug,
-    error,
-};
-
-/// Takes a subgraph generated by a Generator and serializes it so it can be returned by a
-/// lambda as an execution result.
-#[derive(Clone, Debug, Default)]
-pub struct GraphDescriptionSerializer {
-    proto: Vec<u8>,
-    compressed: Vec<u8>,
-}
-
-impl GraphDescriptionSerializer {
-    pub fn new(proto: Vec<u8>, compressed: Vec<u8>) -> Self {
-        Self { proto, compressed }
-    }
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum GraphDescriptionSerializerError {
-    #[error("IO")]
-    Io(#[from] std::io::Error),
-    #[error("EncodeError")]
-    EncodeError(#[from] EncodeError),
-}
-
-impl CompletionEventSerializer for GraphDescriptionSerializer {
-    type CompletedEvent = GraphDescription;
-    type Output = Vec<u8>;
-    type Error = GraphDescriptionSerializerError;
-
-    fn serialize_completed_events(
-        &mut self,
-        completed_events: &[Self::CompletedEvent],
-    ) -> Result<Vec<Self::Output>, Self::Error> {
-        let mut subgraph = Self::CompletedEvent::new();
-
-        // counts nodes and edges before subgraphs are merged
-        let mut pre_nodes = 0;
-        let mut pre_edges = 0;
-
-        for sg in completed_events.iter() {
-            pre_nodes += sg.nodes.len();
-            pre_edges += sg.edges.len();
-            subgraph.merge(sg);
-        }
-
-        if subgraph.is_empty() {
-            debug!(
-                concat!(
-                    "Output subgraph is empty. Serializing to empty vector.",
-                    "pre_nodes: {} pre_edges: {}"
-                ),
-                pre_nodes, pre_edges,
-            );
-            return Ok(vec![]);
-        }
-
-        for edge_list in subgraph.edges.values_mut() {
-            edge_list.edges.sort_unstable();
-            edge_list.edges.dedup();
-        }
-
-        // TODO: This should be a metric
-        debug!(
-            "Serializing {} nodes {} edges. Down from {} nodes {} edges.",
-            subgraph.nodes.len(),
-            subgraph.edges.len(),
-            pre_nodes,
-            pre_edges,
-        );
-
-        self.proto.clear();
-        self.compressed.clear();
-
-        // encode generated subgraphs into protocol buffer
-        prost::Message::encode(&subgraph, &mut self.proto)?;
-
-        let mut compressed = &mut self.compressed;
-        let mut proto = std::io::Cursor::new(&self.proto);
-
-        // compress encoded subgraph into `compressed` vector
-        zstd::stream::copy_encode(&mut proto, &mut compressed, 4)?;
-
-        Ok(vec![compressed.clone()])
-    }
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum IdentifiedGraphSerializerError {
-    #[error("IO")]
-    Io(#[from] std::io::Error),
-    #[error("EncodeError")]
-    EncodeError(#[from] EncodeError),
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct IdentifiedGraphSerializer {
-    proto: Vec<u8>,
-    compressed: Vec<u8>,
-}
-
-impl IdentifiedGraphSerializer {
-    pub fn new(proto: Vec<u8>, compressed: Vec<u8>) -> Self {
-        Self { proto, compressed }
-    }
-}
-
-impl CompletionEventSerializer for IdentifiedGraphSerializer {
-    type CompletedEvent = IdentifiedGraph;
-    type Output = Vec<u8>;
-    type Error = IdentifiedGraphSerializerError;
-
-    fn serialize_completed_events(
-        &mut self,
-        completed_events: &[Self::CompletedEvent],
-    ) -> Result<Vec<Self::Output>, Self::Error> {
-        let mut subgraph = Self::CompletedEvent::new();
-
-        // counts nodes and edges before subgraphs are merged
-        let mut pre_nodes = 0;
-        let mut pre_edges = 0;
-
-        for sg in completed_events.iter() {
-            pre_nodes += sg.nodes.len();
-            pre_edges += sg.edges.len();
-            subgraph.merge(sg);
-        }
-
-        if subgraph.is_empty() {
-            debug!(
-                concat!(
-                    "Output subgraph is empty. Serializing to empty vector.",
-                    "pre_nodes: {} pre_edges: {}"
-                ),
-                pre_nodes, pre_edges,
-            );
-            return Ok(vec![]);
-        }
-
-        for edge_list in subgraph.edges.values_mut() {
-            edge_list.edges.sort_unstable();
-            edge_list.edges.dedup();
-        }
-        // TODO: This should be a metric
-        debug!(
-            "Serializing {} nodes {} edges. Down from {} nodes {} edges.",
-            subgraph.nodes.len(),
-            subgraph.edges.len(),
-            pre_nodes,
-            pre_edges,
-        );
-
-        self.proto.clear();
-        self.compressed.clear();
-
-        // encode generated subgraphs into protocol buffer
-        prost::Message::encode(&subgraph, &mut self.proto)?;
-
-        let mut compressed = &mut self.compressed;
-        let mut proto = std::io::Cursor::new(&self.proto);
-
-        // compress encoded subgraph into `compressed` vector
-        zstd::stream::copy_encode(&mut proto, &mut compressed, 4)?;
-
-        Ok(vec![compressed.clone()])
-    }
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum MergedGraphSerializerError {
-    #[error("IO")]
-    Io(#[from] std::io::Error),
-    #[error("EncodeError")]
-    EncodeError(#[from] EncodeError),
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MergedGraphSerializer {
-    proto: Vec<u8>,
-    compressed: Vec<u8>,
-}
-
-impl MergedGraphSerializer {
-    pub fn new(proto: Vec<u8>, compressed: Vec<u8>) -> Self {
-        Self { proto, compressed }
-    }
-}
-
-impl CompletionEventSerializer for MergedGraphSerializer {
-    type CompletedEvent = MergedGraph;
-    type Output = Vec<u8>;
-    type Error = MergedGraphSerializerError;
-
-    fn serialize_completed_events(
-        &mut self,
-        completed_events: &[Self::CompletedEvent],
-    ) -> Result<Vec<Self::Output>, Self::Error> {
-        let mut subgraph = Self::CompletedEvent::new();
-
-        // counts nodes and edges before subgraphs are merged
-        let mut pre_nodes = 0;
-        let mut pre_edges = 0;
-
-        for sg in completed_events.iter() {
-            pre_nodes += sg.nodes.len();
-            pre_edges += sg.edges.len();
-            subgraph.merge(sg);
-        }
-
-        if subgraph.is_empty() {
-            debug!(
-                concat!(
-                    "Output subgraph is empty. Serializing to empty vector.",
-                    "pre_nodes: {} pre_edges: {}"
-                ),
-                pre_nodes, pre_edges,
-            );
-            return Ok(vec![]);
-        }
-        for edge_list in subgraph.edges.values_mut() {
-            edge_list.edges.sort_unstable();
-            edge_list.edges.dedup();
-        }
-        // TODO: This should be a metric
-        debug!(
-            "Serializing {} nodes {} edges. Down from {} nodes {} edges.",
-            subgraph.nodes.len(),
-            subgraph.edges.len(),
-            pre_nodes,
-            pre_edges,
-        );
-
-        self.proto.clear();
-        self.compressed.clear();
-
-        // encode generated subgraphs into protocol buffer
-        prost::Message::encode(&subgraph, &mut self.proto)?;
-
-        let mut compressed = &mut self.compressed;
-        let mut proto = std::io::Cursor::new(&self.proto);
-
-        // compress encoded subgraph into `compressed` vector
-        zstd::stream::copy_encode(&mut proto, &mut compressed, 4)?;
-
-        Ok(vec![compressed.clone()])
-    }
-}
diff --git a/src/rust/grapl/Cargo.toml b/src/rust/grapl/Cargo.toml
deleted file mode 100644
index a452e0985e..0000000000
--- a/src/rust/grapl/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "grapl"
-version = "0.3.1" # The next-to-be-released tag
-authors = ["Max Wittek <wimax@graplsecurity.com>"]
-edition = "2021"
-description = "One crate for all your Grapl needs"
-license = "Apache-2.0"
-
-[dependencies]
-derive-dynamic-node = { path = "../derive-dynamic-node" }
-graph-generator-lib = { path = "../generators/graph-generator-lib/" }
-grapl-config = { path = "../grapl-config" }
-rust-proto = { path = "../rust-proto" }
diff --git a/src/rust/grapl/src/lib.rs b/src/rust/grapl/src/lib.rs
deleted file mode 100644
index 30b8686c61..0000000000
--- a/src/rust/grapl/src/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-pub use derive_dynamic_node;
-pub use graph_generator_lib as generator_lib;
-pub use grapl_config as config;
-pub use rust_proto as graph_descriptions;
diff --git a/src/rust/kafka/Cargo.toml b/src/rust/kafka/Cargo.toml
index c3224daf4f..5640a69148 100644
--- a/src/rust/kafka/Cargo.toml
+++ b/src/rust/kafka/Cargo.toml
@@ -22,6 +22,6 @@ rdkafka = { version = "0.28", features = [
   "tokio",
   "zstd"
 ] }
-rust-proto-new = { path = "../rust-proto-new", version = "*" }
+rust-proto = { path = "../rust-proto", version = "*" }
 thiserror = "1.0"
 tracing = "0.1"
diff --git a/src/rust/kafka/src/lib.rs b/src/rust/kafka/src/lib.rs
index b3d7409360..1f44cef5ef 100644
--- a/src/rust/kafka/src/lib.rs
+++ b/src/rust/kafka/src/lib.rs
@@ -27,7 +27,7 @@ use rdkafka::{
     util::Timeout,
     Message,
 };
-use rust_proto_new::{
+use rust_proto::{
     SerDe,
     SerDeError,
 };
diff --git a/src/rust/model-plugin-deployer/Cargo.toml b/src/rust/model-plugin-deployer/Cargo.toml
index f480be99b7..b28e44b9fd 100644
--- a/src/rust/model-plugin-deployer/Cargo.toml
+++ b/src/rust/model-plugin-deployer/Cargo.toml
@@ -32,7 +32,6 @@ tonic = "0.6"
 tower = "0.4.11"
 # Tracing support
 tracing = "0.1.29"
-tracing-futures = "0.2.5"
 tracing-subscriber = "0.3.2"
 # Health Checks
 tonic-health = "0.5.0"
@@ -49,4 +48,4 @@ async-trait = "0.1.51"
 tonic-build = { version = "0.6.0", features = ["prost"] }
 
 [features]
-integration = []
+integration_tests = []
diff --git a/src/rust/node-identifier/Cargo.toml b/src/rust/node-identifier/Cargo.toml
index db9d616461..711eb75ab4 100644
--- a/src/rust/node-identifier/Cargo.toml
+++ b/src/rust/node-identifier/Cargo.toml
@@ -13,7 +13,7 @@ name = "node-identifier"
 path = "src/main.rs"
 
 [features]
-new_integration_tests = []
+integration_tests = []
 
 [dependencies]
 bytes = "1.1.0"
@@ -42,7 +42,7 @@ rusoto_dynamodb = { version = "0.47.0", default_features = false, features = [
   "rustls"
 ] }
 rusoto_credential = "0.47.0"
-rust-proto-new = { path = "../rust-proto-new", version = "*" }
+rust-proto = { path = "../rust-proto", version = "*" }
 serde = "1.0"
 serde_dynamodb = { version = "0.9.0", default_features = false, features = [
   "rustls"
diff --git a/src/rust/node-identifier/src/dynamic_sessiondb.rs b/src/rust/node-identifier/src/dynamic_sessiondb.rs
index 7cdd05959c..baa33b8850 100644
--- a/src/rust/node-identifier/src/dynamic_sessiondb.rs
+++ b/src/rust/node-identifier/src/dynamic_sessiondb.rs
@@ -8,7 +8,7 @@ use failure::{
     Error,
 };
 use rusoto_dynamodb::DynamoDb;
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     GraphDescription,
     NodeDescription,
     Session,
diff --git a/src/rust/node-identifier/src/main.rs b/src/rust/node-identifier/src/main.rs
index 576d4df9b5..9f50def80a 100644
--- a/src/rust/node-identifier/src/main.rs
+++ b/src/rust/node-identifier/src/main.rs
@@ -14,7 +14,7 @@ use opentelemetry::{
     sdk::propagation::TraceContextPropagator,
 };
 use rusoto_dynamodb::DynamoDbClient;
-use rust_proto_new::graplinc::grapl::{
+use rust_proto::graplinc::grapl::{
     api::graph::v1beta1::{
         GraphDescription,
         IdentifiedGraph,
diff --git a/src/rust/node-identifier/src/node_identifier.rs b/src/rust/node-identifier/src/node_identifier.rs
index 5fda3b62cf..5f751eb1ff 100644
--- a/src/rust/node-identifier/src/node_identifier.rs
+++ b/src/rust/node-identifier/src/node_identifier.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
 
 use failure::Error;
 use grapl_utils::rusoto_ext::dynamodb::GraplDynamoDbClientExt;
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     GraphDescription,
     IdentifiedGraph,
     IdentifiedNode,
diff --git a/src/rust/organization-management/Cargo.toml b/src/rust/organization-management/Cargo.toml
index 6acff2b00f..8116c6b5d9 100644
--- a/src/rust/organization-management/Cargo.toml
+++ b/src/rust/organization-management/Cargo.toml
@@ -11,10 +11,9 @@ name = "organization-management"
 path = "src/main.rs"
 
 [dependencies]
+async-trait = "0.1"
 grapl-config = { path = "../grapl-config" }
 grapl-utils = { path = "../grapl-utils" }
-tonic = "0.6"
-prost = "0.9.0"
 tokio = { version = "1.17", features = ["macros", "rt-multi-thread"] }
 uuid = { version = "0.8", features = ["serde", "v4"] }
 sqlx = { version = "0.5", features = [
@@ -24,7 +23,6 @@ sqlx = { version = "0.5", features = [
   "uuid",
   "migrate",
 ] }
-tracing-futures = "0.2.5"
 tracing = "0.1.29"
 tracing-subscriber = { version = "0.3", default-features = false, features = [
   "env-filter",
@@ -39,11 +37,8 @@ clap = { version = "3.0", default_features = false, features = [
   "derive"
 ] }
 
-[build-dependencies]
-tonic-build = { version = "0.6.0", features = ["prost"] }
-
 [dev-dependencies]
 test-log = { version = "0.2", default-features = false, features = ["trace"] }
 
 [features]
-integration = []
+integration_tests = []
diff --git a/src/rust/organization-management/src/client.rs b/src/rust/organization-management/src/client.rs
deleted file mode 100644
index 3ff24aeb49..0000000000
--- a/src/rust/organization-management/src/client.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-use std::{
-    convert::TryInto,
-    time::Duration,
-};
-
-use rust_proto::organization_management::{
-    organization_management_service_client::OrganizationManagementServiceClient as OrganizationManagementServiceProto,
-    CreateOrganizationRequest,
-    CreateOrganizationRequestProto,
-    CreateOrganizationResponse,
-    CreateUserRequest,
-    CreateUserRequestProto,
-    CreateUserResponse,
-    OrganizationManagementDeserializationError,
-};
-use tonic::{
-    codegen::{
-        Body,
-        StdError,
-    },
-    transport::Endpoint,
-    Status,
-};
-
-const ADDRESS_ENV_VAR: &'static str = "ORGANIZATION_MANAGEMENT_ADDRESS";
-
-#[derive(Debug, thiserror::Error)]
-pub enum OrganizationManagementServiceClientError {
-    #[error("GrpcStatus {0}")]
-    GrpcStatus(#[from] Status),
-    #[error("DeserializeError {0}")]
-    DeserializeError(#[from] OrganizationManagementDeserializationError),
-}
-
-#[derive(Debug)]
-pub struct OrganizationManagementServiceClient<T> {
-    inner: OrganizationManagementServiceProto<T>,
-}
-
-impl OrganizationManagementServiceClient<tonic::transport::Channel> {
-    /// Create a client from environment
-    #[tracing::instrument(err)]
-    pub async fn from_env() -> Result<Self, Box<dyn std::error::Error>> {
-        let address = std::env::var(ADDRESS_ENV_VAR).expect(ADDRESS_ENV_VAR);
-        Self::from_endpoint(address).await
-    }
-
-    /// Create a client from a specific endpoint
-    #[tracing::instrument(err)]
-    pub async fn from_endpoint(address: String) -> Result<Self, Box<dyn std::error::Error>> {
-        tracing::debug!(message = "Connecting to organization management endpoint");
-
-        // TODO: It might make sense to make these values configurable.
-        let endpoint = Endpoint::from_shared(address)?
-            .timeout(Duration::from_secs(5))
-            .concurrency_limit(30);
-        let channel = endpoint.connect().await?;
-        Ok(Self::new(OrganizationManagementServiceProto::new(channel)))
-    }
-}
-
-impl<T> OrganizationManagementServiceClient<T>
-where
-    T: tonic::client::GrpcService<tonic::body::BoxBody>,
-    T::ResponseBody: Body + Send + 'static,
-    T::Error: Into<StdError>,
-    <T::ResponseBody as Body>::Error: Into<StdError> + Send,
-{
-    pub fn new(inner: OrganizationManagementServiceProto<T>) -> Self {
-        Self { inner }
-    }
-
-    /// Create a new organization
-    pub async fn create_organization(
-        &mut self,
-        request: CreateOrganizationRequest,
-    ) -> Result<CreateOrganizationResponse, OrganizationManagementServiceClientError> {
-        let response = self
-            .inner
-            .create_organization(CreateOrganizationRequestProto::from(request))
-            .await?;
-        Ok(response.into_inner().try_into()?)
-    }
-
-    /// Creates a new user
-    pub async fn create_user(
-        &mut self,
-        request: CreateUserRequest,
-    ) -> Result<CreateUserResponse, OrganizationManagementServiceClientError> {
-        let response = self
-            .inner
-            .create_user(CreateUserRequestProto::from(request))
-            .await?;
-        Ok(response.into_inner().try_into()?)
-    }
-}
diff --git a/src/rust/organization-management/src/lib.rs b/src/rust/organization-management/src/lib.rs
index 471f4785fc..0ec1955a75 100644
--- a/src/rust/organization-management/src/lib.rs
+++ b/src/rust/organization-management/src/lib.rs
@@ -1,6 +1,5 @@
 use std::net::SocketAddr;
 
-pub mod client;
 pub mod server;
 
 #[derive(clap::Parser, Debug)]
@@ -8,6 +7,8 @@ pub struct OrganizationManagementServiceConfig {
     #[clap(long, env)]
     pub organization_management_bind_address: SocketAddr,
     #[clap(long, env)]
+    pub organization_management_healthcheck_polling_interval_ms: u64,
+    #[clap(long, env)]
     pub organization_management_db_hostname: String,
     #[clap(long, env)]
     pub organization_management_db_port: u16,
diff --git a/src/rust/organization-management/src/server.rs b/src/rust/organization-management/src/server.rs
index 4200d814c6..b503e57aa6 100644
--- a/src/rust/organization-management/src/server.rs
+++ b/src/rust/organization-management/src/server.rs
@@ -1,4 +1,4 @@
-use std::convert::TryFrom;
+use std::time::Duration;
 
 use argon2::{
     password_hash::{
@@ -8,31 +8,28 @@ use argon2::{
     PasswordHasher,
 };
 use grapl_utils::future_ext::GraplFutureExt;
-use rust_proto::organization_management::{
-    organization_management_service_server::{
-        OrganizationManagementService,
-        OrganizationManagementServiceServer,
+use rust_proto::{
+    graplinc::grapl::api::organization_management::v1beta1::{
+        server::{
+            ConfigurationError as ServerConfigurationError,
+            OrganizationManagementApi,
+            OrganizationManagementServer,
+        },
+        CreateOrganizationRequest,
+        CreateOrganizationResponse,
+        CreateUserRequest,
+        CreateUserResponse,
+    },
+    protocol::{
+        healthcheck::HealthcheckStatus,
+        status::Status,
     },
-    CreateOrganizationRequest,
-    CreateOrganizationRequestProto,
-    CreateOrganizationResponse,
-    CreateOrganizationResponseProto,
-    CreateUserRequest,
-    CreateUserRequestProto,
-    CreateUserResponse,
-    CreateUserResponseProto,
-    OrganizationManagementDeserializationError,
 };
 use sqlx::{
     postgres::Postgres,
     Pool,
 };
-use tonic::{
-    transport::Server,
-    Request,
-    Response,
-    Status,
-};
+use tokio::net::TcpListener;
 use uuid::Uuid;
 
 use crate::OrganizationManagementServiceConfig;
@@ -41,10 +38,10 @@ use crate::OrganizationManagementServiceConfig;
 pub enum OrganizationManagementServiceError {
     #[error("Sql {0}")]
     Sql(#[from] sqlx::Error),
-    #[error("OrganizationanizationManagementDeserializationError {0}")]
-    OrganizationManagementDeserializationError(#[from] OrganizationManagementDeserializationError),
     #[error("HashError {0}")]
     HashError(String),
+    #[error("ServerError {0}")]
+    ServerError(#[from] ServerConfigurationError),
 }
 
 impl From<argon2::Error> for OrganizationManagementServiceError {
@@ -63,9 +60,6 @@ impl From<OrganizationManagementServiceError> for Status {
     fn from(e: OrganizationManagementServiceError) -> Self {
         match e {
             OrganizationManagementServiceError::Sql(e) => Status::internal(e.to_string()),
-            OrganizationManagementServiceError::OrganizationManagementDeserializationError(e) => {
-                Status::invalid_argument(e.to_string())
-            }
             _ => todo!(),
         }
     }
@@ -90,10 +84,11 @@ impl OrganizationManagement {
 
         Ok(Self {
             pool: sqlx::PgPool::connect(&postgres_address)
-                .timeout(std::time::Duration::from_secs(5))
+                .timeout(Duration::from_secs(5))
                 .await??,
         })
     }
+
     async fn create_organization(
         &self,
         request: CreateOrganizationRequest,
@@ -218,32 +213,36 @@ impl OrganizationManagement {
     }
 }
 
-#[tonic::async_trait]
-impl OrganizationManagementService for OrganizationManagement {
+pub struct ManagementApi {
+    organization_management: OrganizationManagement,
+}
+
+impl ManagementApi {
+    pub fn new(organization_management: OrganizationManagement) -> Self {
+        ManagementApi {
+            organization_management,
+        }
+    }
+}
+
+#[async_trait::async_trait]
+impl OrganizationManagementApi for ManagementApi {
+    type Error = OrganizationManagementServiceError;
+
     async fn create_organization(
         &self,
-        request: Request<CreateOrganizationRequestProto>,
-    ) -> Result<Response<CreateOrganizationResponseProto>, Status> {
-        let request: CreateOrganizationRequestProto = request.into_inner();
-        let request = CreateOrganizationRequest::try_from(request)
-            .map_err(OrganizationManagementServiceError::from)?;
-
-        let response = self.create_organization(request).await?;
-        let response: CreateOrganizationResponseProto = response.into();
-        Ok(Response::new(response))
+        request: CreateOrganizationRequest,
+    ) -> Result<CreateOrganizationResponse, Self::Error> {
+        self.organization_management
+            .create_organization(request)
+            .await
     }
 
     async fn create_user(
         &self,
-        request: Request<CreateUserRequestProto>,
-    ) -> Result<Response<CreateUserResponseProto>, Status> {
-        let request: CreateUserRequestProto = request.into_inner();
-        let request = CreateUserRequest::try_from(request)
-            .map_err(OrganizationManagementServiceError::from)?;
-
-        let response = self.create_user(request).await?;
-        let response: CreateUserResponseProto = response.into();
-        Ok(Response::new(response))
+        request: CreateUserRequest,
+    ) -> Result<CreateUserResponse, Self::Error> {
+        self.organization_management.create_user(request).await
     }
 }
 
@@ -258,21 +257,14 @@ pub async fn exec_service(
 
     tracing::info!(message = "Binding service",);
 
-    Server::builder()
-        .trace_fn(|request| {
-            tracing::info_span!(
-                "OrganizationManagement",
-                headers = ?request.headers(),
-                method = ?request.method(),
-                uri = %request.uri(),
-                extensions = ?request.extensions(),
-            )
-        })
-        .add_service(OrganizationManagementServiceServer::new(
-            organization_management,
-        ))
-        .serve(service_config.organization_management_bind_address)
-        .await?;
+    let (server, _shutdown_tx) = OrganizationManagementServer::new(
+        ManagementApi::new(organization_management),
+        TcpListener::bind(service_config.organization_management_bind_address).await?,
+        || async { Ok(HealthcheckStatus::Serving) }, // FIXME: this is garbage
+        Duration::from_millis(
+            service_config.organization_management_healthcheck_polling_interval_ms,
+        ),
+    );
 
-    Ok(())
+    Ok(server.serve().await?)
 }
diff --git a/src/rust/pipeline-ingress/Cargo.toml b/src/rust/pipeline-ingress/Cargo.toml
index badeba18b9..d42ef40517 100644
--- a/src/rust/pipeline-ingress/Cargo.toml
+++ b/src/rust/pipeline-ingress/Cargo.toml
@@ -17,7 +17,7 @@ opentelemetry-jaeger = { version = "0.15", features = [
   "collector_client",
   "rt-tokio"
 ] }
-rust-proto-new = { path = "../rust-proto-new", version = "*" }
+rust-proto = { path = "../rust-proto", version = "*" }
 tokio = { version = "1.17", features = ["macros", "rt", "rt-multi-thread"] }
 thiserror = "1.0"
 tracing = "0.1"
@@ -33,4 +33,4 @@ test-context = "0.1"
 tokio = { version = "1.17", features = ["macros", "rt"] }
 
 [features]
-new_integration_tests = []
+integration_tests = []
diff --git a/src/rust/pipeline-ingress/src/main.rs b/src/rust/pipeline-ingress/src/main.rs
index aa5625b4a1..b383e3a0af 100644
--- a/src/rust/pipeline-ingress/src/main.rs
+++ b/src/rust/pipeline-ingress/src/main.rs
@@ -19,7 +19,7 @@ use opentelemetry::{
     sdk::propagation::TraceContextPropagator,
     trace::TraceError,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::{
         api::pipeline_ingress::v1beta1::{
             server::{
diff --git a/src/rust/plugin-bootstrap/Cargo.toml b/src/rust/plugin-bootstrap/Cargo.toml
index c17afc2f15..0a742b8689 100644
--- a/src/rust/plugin-bootstrap/Cargo.toml
+++ b/src/rust/plugin-bootstrap/Cargo.toml
@@ -15,7 +15,7 @@ path = "src/plugin_bootstrap_init.rs"
 name = "plugin_bootstrap"
 
 [dependencies]
-async-trait = "0.1.51"
+async-trait = "0.1"
 clap = { version = "3.0", default_features = false, features = [
   "std",
   "env",
@@ -24,11 +24,9 @@ clap = { version = "3.0", default_features = false, features = [
 grapl-config = { path = "../grapl-config" }
 grapl-utils = { path = "../grapl-utils" }
 rust-proto = { path = "../rust-proto" }
-thiserror = "1.0.30"
+thiserror = "1.0"
 tokio = { version = "1.14.0", features = ["full"] }
-tonic = "0.6"
-tonic-health = "0.5.0"
-tracing = "0.1.29"
+tracing = "0.1"
 uuid = { version = "0.8", features = ["v4"] }
 futures = "0.3"
 
diff --git a/src/rust/plugin-bootstrap/src/client.rs b/src/rust/plugin-bootstrap/src/client.rs
index d09e80cba8..e69de29bb2 100644
--- a/src/rust/plugin-bootstrap/src/client.rs
+++ b/src/rust/plugin-bootstrap/src/client.rs
@@ -1,76 +0,0 @@
-use rust_proto::plugin_bootstrap::{
-    GetBootstrapRequest,
-    GetBootstrapRequestProto,
-    GetBootstrapResponse,
-    PluginBootstrapDeserializationError,
-    PluginBootstrapServiceClient as PluginBootstrapClientProto,
-};
-use tonic::{
-    codegen::{
-        Body,
-        StdError,
-    },
-    transport::Endpoint,
-};
-
-const ADDRESS_ENV_VAR: &str = "GRAPL_PLUGIN_BOOTSTRAP_ADDRESS";
-
-#[derive(Debug, thiserror::Error)]
-pub enum PluginBootstrapClientError {
-    #[error("GrpcStatus {0}")]
-    Status(#[from] tonic::Status),
-    #[error("PluginBootstrapDeserializationError {0}")]
-    PluginBootstrapDeserializationError(#[from] PluginBootstrapDeserializationError),
-}
-
-#[derive(Debug, Clone)]
-pub struct PluginBootstrapClient<T> {
-    inner: PluginBootstrapClientProto<T>,
-}
-
-impl PluginBootstrapClient<tonic::transport::Channel> {
-    /// Create a client from environment
-    #[tracing::instrument(err)]
-    pub async fn from_env() -> Result<Self, Box<dyn std::error::Error>> {
-        let address = std::env::var(ADDRESS_ENV_VAR).expect(ADDRESS_ENV_VAR);
-        Self::from_endpoint(address).await
-    }
-
-    /// Create a client from a specific endpoint
-    #[tracing::instrument(err)]
-    pub async fn from_endpoint(address: String) -> Result<Self, Box<dyn std::error::Error>> {
-        tracing::debug!(message = "Connecting to endpoint");
-
-        // TODO: It might make sense to make these values configurable.
-        let endpoint = Endpoint::from_shared(address)?
-            .timeout(std::time::Duration::from_secs(5))
-            .concurrency_limit(30);
-        let channel = endpoint.connect().await?;
-        Ok(Self::new(PluginBootstrapClientProto::new(channel)))
-    }
-}
-
-impl<T> PluginBootstrapClient<T>
-where
-    T: tonic::client::GrpcService<tonic::body::BoxBody>,
-    T::ResponseBody: Body + Send + 'static,
-    T::Error: Into<StdError>,
-    <T::ResponseBody as Body>::Error: Into<StdError> + Send,
-{
-    pub fn new(inner: PluginBootstrapClientProto<T>) -> Self {
-        Self { inner }
-    }
-
-    #[tracing::instrument(skip(self))]
-    pub async fn get_bootstrap(
-        &mut self,
-        request: GetBootstrapRequest,
-    ) -> Result<GetBootstrapResponse, PluginBootstrapClientError> {
-        let response = self
-            .inner
-            .get_bootstrap(GetBootstrapRequestProto::from(request))
-            .await?;
-        let response = response.into_inner();
-        Ok(response.try_into()?)
-    }
-}
diff --git a/src/rust/plugin-bootstrap/src/lib.rs b/src/rust/plugin-bootstrap/src/lib.rs
index 694c4351d1..4eee887262 100644
--- a/src/rust/plugin-bootstrap/src/lib.rs
+++ b/src/rust/plugin-bootstrap/src/lib.rs
@@ -1,6 +1,5 @@
 use std::net::SocketAddr;
 
-pub mod client;
 pub mod server;
 
 #[derive(clap::Parser, Debug)]
@@ -11,4 +10,6 @@ pub struct PluginBootstrapServiceConfig {
     pub plugin_binary_path: std::path::PathBuf,
     #[clap(long, env)]
     pub plugin_certificate_path: std::path::PathBuf,
+    #[clap(long, env)]
+    pub plugin_registry_polling_interval_ms: u64,
 }
diff --git a/src/rust/plugin-bootstrap/src/plugin_bootstrap_init.rs b/src/rust/plugin-bootstrap/src/plugin_bootstrap_init.rs
index 03d12361b3..778533c506 100644
--- a/src/rust/plugin-bootstrap/src/plugin_bootstrap_init.rs
+++ b/src/rust/plugin-bootstrap/src/plugin_bootstrap_init.rs
@@ -1,9 +1,15 @@
-use std::os::unix::fs::PermissionsExt;
+use std::{
+    os::unix::fs::PermissionsExt,
+    time::Duration,
+};
 
-use plugin_bootstrap::client::PluginBootstrapClient;
-use rust_proto::plugin_bootstrap::{
-    GetBootstrapRequest,
-    GetBootstrapResponse,
+use rust_proto::{
+    graplinc::grapl::api::plugin_bootstrap::v1beta1::{
+        client::PluginBootstrapClient,
+        GetBootstrapRequest,
+        GetBootstrapResponse,
+    },
+    protocol::healthcheck::client::HealthcheckClient,
 };
 
 static PLUGIN_BINARY_PATH: &str = "/usr/local/bin/grapl-plugin";
@@ -14,7 +20,25 @@ static PLUGIN_CONFIG_PATH: &str = "/etc/systemd/system/plugin.service.d/override
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
     let (_env, _guard) = grapl_config::init_grapl_env!();
 
-    let mut bootstrap_client = PluginBootstrapClient::from_env().await?;
+    let endpoint = std::env::var("PLUGIN_BOOTSTRAP_CLIENT_ADDRESS")?;
+    let plugin_bootstrap_polling_interval_ms: u64 =
+        std::env::var("PLUGIN_BOOTSTRAP_POLLING_INTERVAL_MS")?.parse()?;
+
+    tracing::info!(
+        message = "waiting 5s for plugin-bootstrap to report healthy",
+        endpoint = %endpoint,
+    );
+
+    HealthcheckClient::wait_until_healthy(
+        endpoint.clone(),
+        "graplinc.grapl.api.plugin_bootstrap.v1beta1.PluginBootstrapService",
+        Duration::from_secs(5), // TODO: parametrize
+        Duration::from_millis(plugin_bootstrap_polling_interval_ms),
+    )
+    .await?;
+
+    let mut bootstrap_client = PluginBootstrapClient::connect(endpoint.clone()).await?;
+
     let GetBootstrapResponse {
         plugin_payload,
         client_certificate,
diff --git a/src/rust/plugin-bootstrap/src/plugin_bootstrap_service.rs b/src/rust/plugin-bootstrap/src/plugin_bootstrap_service.rs
index b9f39dfe10..1e94f60e08 100644
--- a/src/rust/plugin-bootstrap/src/plugin_bootstrap_service.rs
+++ b/src/rust/plugin-bootstrap/src/plugin_bootstrap_service.rs
@@ -1,8 +1,18 @@
+use std::time::Duration;
+
 use clap::Parser;
 use plugin_bootstrap::{
-    server::PluginBootstrapper,
+    server::{
+        PluginBootstrap,
+        PluginBootstrapper,
+    },
     PluginBootstrapServiceConfig,
 };
+use rust_proto::{
+    graplinc::grapl::api::plugin_bootstrap::v1beta1::server::PluginBootstrapServer,
+    protocol::healthcheck::HealthcheckStatus,
+};
+use tokio::net::TcpListener;
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -13,7 +23,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     let plugin_bootstrapper =
         PluginBootstrapper::load(&config.plugin_certificate_path, &config.plugin_binary_path)?;
 
-    plugin_bootstrapper.serve(config).await?;
+    let plugin_bootstrap = PluginBootstrap::new(plugin_bootstrapper);
+
+    let (server, _shutdown_tx) = PluginBootstrapServer::new(
+        plugin_bootstrap,
+        TcpListener::bind(config.plugin_registry_bind_address).await?,
+        || async { Ok(HealthcheckStatus::Serving) }, // FIXME; this is garbage
+        Duration::from_millis(config.plugin_registry_polling_interval_ms),
+    );
 
-    Ok(())
+    Ok(server.serve().await?)
 }
diff --git a/src/rust/plugin-bootstrap/src/server.rs b/src/rust/plugin-bootstrap/src/server.rs
index e8e7556b80..3dd33108a9 100644
--- a/src/rust/plugin-bootstrap/src/server.rs
+++ b/src/rust/plugin-bootstrap/src/server.rs
@@ -3,26 +3,36 @@ use std::{
     sync::atomic::Ordering,
 };
 
-use rust_proto::plugin_bootstrap::{
-    ClientCertificate,
-    GetBootstrapRequestProto,
-    GetBootstrapResponse,
-    GetBootstrapResponseProto,
-    PluginBootstrapService,
-    PluginBootstrapServiceServer,
-    PluginPayload,
+use rust_proto::{
+    graplinc::grapl::api::plugin_bootstrap::v1beta1::{
+        server::{
+            ConfigurationError as ServerConfigurationError,
+            PluginBootstrapApi,
+        },
+        ClientCertificate,
+        GetBootstrapRequest,
+        GetBootstrapResponse,
+        PluginPayload,
+    },
+    protocol::status::Status,
 };
-use tonic::{
-    transport::Server,
-    Status,
-};
-
-use crate::PluginBootstrapServiceConfig;
+use thiserror::Error;
 
-#[derive(Debug, thiserror::Error)]
-pub enum PluginBootstrapperError {
+#[derive(Debug, Error)]
+pub enum PluginBootstrapError {
     #[error("IoError {0}")]
     IoError(#[from] std::io::Error),
+    #[error("ServerError {0}")]
+    ServerError(#[from] ServerConfigurationError),
+}
+
+impl From<PluginBootstrapError> for Status {
+    fn from(e: PluginBootstrapError) -> Self {
+        match e {
+            PluginBootstrapError::IoError(e) => Status::internal(e.to_string()),
+            PluginBootstrapError::ServerError(e) => Status::internal(e.to_string()),
+        }
+    }
 }
 
 pub struct PluginBootstrapper {
@@ -43,7 +53,7 @@ impl PluginBootstrapper {
     pub fn load(
         certificate_path: &std::path::Path,
         plugin_binary_path: &std::path::Path,
-    ) -> Result<Self, PluginBootstrapperError> {
+    ) -> Result<Self, PluginBootstrapError> {
         let certificate_file = std::fs::File::open(certificate_path)?;
         let plugin_binary_file = std::fs::File::open(plugin_binary_path)?;
 
@@ -52,73 +62,58 @@ impl PluginBootstrapper {
 
         let mut reader = std::io::BufReader::new(certificate_file);
         reader.read_to_end(&mut certificate)?;
+
         let mut reader = std::io::BufReader::new(plugin_binary_file);
         reader.read_to_end(&mut plugin_binary)?;
 
-        let plugin_payload = PluginPayload { plugin_binary };
+        let plugin_payload = PluginPayload {
+            plugin_binary: plugin_binary.into(),
+        };
+
         let client_certificate = ClientCertificate {
-            client_certificate: certificate,
+            client_certificate: certificate.into(),
         };
+
         Ok(PluginBootstrapper::new(client_certificate, plugin_payload))
     }
 
     async fn get_bootstrap(&self) -> GetBootstrapResponse {
         let counter = self.counter.fetch_add(1, Ordering::SeqCst);
+
         if counter != 0 {
             tracing::warn!(
                 message="Bootstrap information has been requested more than once.",
                 count=%counter,
             );
         }
+
         GetBootstrapResponse {
             plugin_payload: self.plugin_payload.clone(),
             client_certificate: self.client_certificate.clone(),
         }
     }
+}
 
-    #[tracing::instrument(skip(self))]
-    pub async fn serve(
-        self,
-        service_config: PluginBootstrapServiceConfig,
-    ) -> Result<(), Box<dyn std::error::Error>> {
-        let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
-        health_reporter
-            .set_serving::<PluginBootstrapServiceServer<PluginBootstrapper>>()
-            .await;
-
-        let addr = service_config.plugin_registry_bind_address;
-        tracing::info!(
-            message="Starting PluginBootstrap",
-            addr=?addr,
-        );
-
-        Server::builder()
-            .trace_fn(|request| {
-                tracing::info_span!(
-                    "PluginBootstrap",
-                    headers = ?request.headers(),
-                    method = ?request.method(),
-                    uri = %request.uri(),
-                    extensions = ?request.extensions(),
-                )
-            })
-            .add_service(health_service)
-            .add_service(PluginBootstrapServiceServer::new(self))
-            .serve(addr)
-            .await?;
-
-        Ok(())
+pub struct PluginBootstrap {
+    plugin_bootstrapper: PluginBootstrapper,
+}
+
+impl PluginBootstrap {
+    pub fn new(plugin_bootstrapper: PluginBootstrapper) -> PluginBootstrap {
+        PluginBootstrap {
+            plugin_bootstrapper,
+        }
     }
 }
 
-#[tonic::async_trait]
-impl PluginBootstrapService for PluginBootstrapper {
-    #[tracing::instrument(skip(self))]
+#[async_trait::async_trait]
+impl PluginBootstrapApi for PluginBootstrap {
+    type Error = PluginBootstrapError;
+
     async fn get_bootstrap(
         &self,
-        _request: tonic::Request<GetBootstrapRequestProto>,
-    ) -> Result<tonic::Response<GetBootstrapResponseProto>, Status> {
-        let response = self.get_bootstrap().await;
-        Ok(tonic::Response::new(response.into()))
+        _request: GetBootstrapRequest,
+    ) -> Result<GetBootstrapResponse, Self::Error> {
+        Ok(self.plugin_bootstrapper.get_bootstrap().await)
     }
 }
diff --git a/src/rust/plugin-executor/Cargo.toml b/src/rust/plugin-executor/Cargo.toml
index 1c4d9859e7..2f54627cf7 100644
--- a/src/rust/plugin-executor/Cargo.toml
+++ b/src/rust/plugin-executor/Cargo.toml
@@ -25,9 +25,9 @@ clap = { version = "3.0", default_features = false, features = [
 generator-sdk = { path = "../plugin-sdk/generator-sdk", features = ["client"] }
 grapl-config = { path = "../grapl-config" }
 plugin-work-queue = { path = "../plugin-work-queue" }
-rust-proto-new = { path = "../rust-proto-new" }
+rust-proto = { path = "../rust-proto" }
 tokio = { version = "1.17", features = ["macros", "rt", "rt-multi-thread"] }
 tracing = "0.1.29"
 
 [features]
-new_integration_tests = []
+integration_tests = []
diff --git a/src/rust/plugin-executor/generator-executor.rs b/src/rust/plugin-executor/generator-executor.rs
index 82e0fc4979..d901aff2cf 100644
--- a/src/rust/plugin-executor/generator-executor.rs
+++ b/src/rust/plugin-executor/generator-executor.rs
@@ -6,7 +6,7 @@ use plugin_work_queue::client::{
     FromEnv,
     PluginWorkQueueServiceClient,
 };
-use rust_proto_new::graplinc::grapl::api::plugin_work_queue::v1beta1;
+use rust_proto::graplinc::grapl::api::plugin_work_queue::v1beta1;
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
diff --git a/src/rust/plugin-registry/Cargo.toml b/src/rust/plugin-registry/Cargo.toml
index 4e41fc39f7..fae6bf67b2 100644
--- a/src/rust/plugin-registry/Cargo.toml
+++ b/src/rust/plugin-registry/Cargo.toml
@@ -21,7 +21,7 @@ clap = { version = "3.0", default_features = false, features = [
 futures-retry = "0.6.0"
 grapl-config = { path = "../grapl-config" }
 grapl-utils = { path = "../grapl-utils" }
-rust-proto-new = { path = "../rust-proto-new" }
+rust-proto = { path = "../rust-proto" }
 nomad-client-gen = { path = "../nomad-client-gen" }
 serde_json = "1.0.72"
 sqlx = { version = "0.5", features = [
@@ -55,4 +55,4 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [
 ] }
 
 [features]
-new_integration_tests = []
+integration_tests = []
diff --git a/src/rust/plugin-registry/src/client.rs b/src/rust/plugin-registry/src/client.rs
index e85642b52f..21cadff317 100644
--- a/src/rust/plugin-registry/src/client.rs
+++ b/src/rust/plugin-registry/src/client.rs
@@ -1,7 +1,7 @@
 use std::time::Duration;
 
 use async_trait;
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::plugin_registry::v1beta1::{
         PluginRegistryServiceClient,
         PluginRegistryServiceClientError,
diff --git a/src/rust/plugin-registry/src/db/client.rs b/src/rust/plugin-registry/src/db/client.rs
index 01186a1f1e..bab2e343b7 100644
--- a/src/rust/plugin-registry/src/db/client.rs
+++ b/src/rust/plugin-registry/src/db/client.rs
@@ -1,5 +1,5 @@
 use grapl_utils::future_ext::GraplFutureExt;
-use rust_proto_new::graplinc::grapl::api::plugin_registry::v1beta1::PluginType;
+use rust_proto::graplinc::grapl::api::plugin_registry::v1beta1::PluginType;
 
 use super::models::{
     PluginDeploymentRow,
diff --git a/src/rust/plugin-registry/src/db/serde.rs b/src/rust/plugin-registry/src/db/serde.rs
index d90a4efc10..e6d4c9bcb8 100644
--- a/src/rust/plugin-registry/src/db/serde.rs
+++ b/src/rust/plugin-registry/src/db/serde.rs
@@ -1,4 +1,4 @@
-use rust_proto_new::graplinc::grapl::api::plugin_registry::v1beta1::PluginType;
+use rust_proto::graplinc::grapl::api::plugin_registry::v1beta1::PluginType;
 use thiserror::Error;
 
 #[derive(Error, Debug)]
diff --git a/src/rust/plugin-registry/src/error.rs b/src/rust/plugin-registry/src/error.rs
index cd09d66897..13fe889aa8 100644
--- a/src/rust/plugin-registry/src/error.rs
+++ b/src/rust/plugin-registry/src/error.rs
@@ -5,7 +5,7 @@ use rusoto_s3::{
     GetObjectError,
     UploadPartError,
 };
-use rust_proto_new::{
+use rust_proto::{
     protocol::status::Status,
     SerDeError,
 };
diff --git a/src/rust/plugin-registry/src/server/create_plugin.rs b/src/rust/plugin-registry/src/server/create_plugin.rs
index ea27ef46dd..de7f671161 100644
--- a/src/rust/plugin-registry/src/server/create_plugin.rs
+++ b/src/rust/plugin-registry/src/server/create_plugin.rs
@@ -13,7 +13,7 @@ use rusoto_s3::{
     UploadPartRequest,
     S3,
 };
-use rust_proto_new::graplinc::grapl::api::plugin_registry::v1beta1::CreatePluginRequest;
+use rust_proto::graplinc::grapl::api::plugin_registry::v1beta1::CreatePluginRequest;
 
 use super::service::PluginRegistryServiceConfig;
 use crate::{
diff --git a/src/rust/plugin-registry/src/server/service.rs b/src/rust/plugin-registry/src/server/service.rs
index cf5ade9954..b7abbfdc88 100644
--- a/src/rust/plugin-registry/src/server/service.rs
+++ b/src/rust/plugin-registry/src/server/service.rs
@@ -10,7 +10,7 @@ use rusoto_s3::{
     S3Client,
     S3,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::plugin_registry::v1beta1::{
         CreatePluginRequest,
         CreatePluginRequestMetadata,
diff --git a/src/rust/plugin-sdk/generator-sdk/Cargo.toml b/src/rust/plugin-sdk/generator-sdk/Cargo.toml
index dc414ac1cd..815a6be7e7 100644
--- a/src/rust/plugin-sdk/generator-sdk/Cargo.toml
+++ b/src/rust/plugin-sdk/generator-sdk/Cargo.toml
@@ -12,7 +12,7 @@ path = "src/examples/example_generator.rs"
 async-trait = "0.1"
 consul-connect = { path = "../../consul-connect" }
 moka = { version = "0.7", features = ["future"] }
-rust-proto-new = { path = "../../rust-proto-new" }
+rust-proto = { path = "../../rust-proto" }
 serde = { version = "1.0.136", features = ["derive"] }
 clap = { version = "3.0", default_features = false, features = [
   "std",
diff --git a/src/rust/plugin-sdk/generator-sdk/src/client.rs b/src/rust/plugin-sdk/generator-sdk/src/client.rs
index da412a36ba..0efaf79aef 100644
--- a/src/rust/plugin-sdk/generator-sdk/src/client.rs
+++ b/src/rust/plugin-sdk/generator-sdk/src/client.rs
@@ -8,7 +8,7 @@ use moka::future::{
     Cache,
     CacheBuilder,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::plugin_sdk::generators::v1beta1::{
         client::{
             GeneratorServiceClient,
diff --git a/src/rust/plugin-sdk/generator-sdk/src/examples/example_generator.rs b/src/rust/plugin-sdk/generator-sdk/src/examples/example_generator.rs
index f4b5d539ee..0e46d246fe 100644
--- a/src/rust/plugin-sdk/generator-sdk/src/examples/example_generator.rs
+++ b/src/rust/plugin-sdk/generator-sdk/src/examples/example_generator.rs
@@ -7,7 +7,7 @@ use generator_sdk::server::{
     self,
     GeneratorServiceConfig,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::{
         graph::v1beta1::GraphDescription,
         plugin_sdk::generators::v1beta1::{
diff --git a/src/rust/plugin-sdk/generator-sdk/src/server.rs b/src/rust/plugin-sdk/generator-sdk/src/server.rs
index 820dd3883e..65cb56c08a 100644
--- a/src/rust/plugin-sdk/generator-sdk/src/server.rs
+++ b/src/rust/plugin-sdk/generator-sdk/src/server.rs
@@ -1,7 +1,7 @@
 use std::time::Duration;
 
 use clap::Parser;
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::plugin_sdk::generators::v1beta1::server::{
         GeneratorApi,
         GeneratorServer,
diff --git a/src/rust/plugin-work-queue/Cargo.toml b/src/rust/plugin-work-queue/Cargo.toml
index b067316623..ab7137d3d9 100644
--- a/src/rust/plugin-work-queue/Cargo.toml
+++ b/src/rust/plugin-work-queue/Cargo.toml
@@ -20,7 +20,7 @@ clap = { version = "3.0", default_features = false, features = [
 ] }
 grapl-config = { path = "../grapl-config" }
 grapl-utils = { path = "../grapl-utils" }
-rust-proto-new = { path = "../rust-proto-new" }
+rust-proto = { path = "../rust-proto" }
 thiserror = "1.0.30"
 tokio = { version = "1.17", features = ["full"] }
 tonic-health = "0.5.0"
@@ -41,5 +41,4 @@ futures = "0.3"
 tracing-subscriber = "0.3"
 
 [features]
-integration = []
-new_integration_tests = []
+integration_tests = []
diff --git a/src/rust/plugin-work-queue/src/client.rs b/src/rust/plugin-work-queue/src/client.rs
index 887bf8338c..a01f2c7a76 100644
--- a/src/rust/plugin-work-queue/src/client.rs
+++ b/src/rust/plugin-work-queue/src/client.rs
@@ -1,5 +1,5 @@
 use async_trait;
-pub use rust_proto_new::graplinc::grapl::api::plugin_work_queue::v1beta1::PluginWorkQueueServiceClient;
+pub use rust_proto::graplinc::grapl::api::plugin_work_queue::v1beta1::PluginWorkQueueServiceClient;
 
 const ADDRESS_ENV_VAR: &'static str = "PLUGIN_WORK_QUEUE_CLIENT_ADDRESS";
 
@@ -15,7 +15,7 @@ impl FromEnv<PluginWorkQueueServiceClient, Box<dyn std::error::Error>>
     /// Create a client from environment
     async fn from_env() -> Result<PluginWorkQueueServiceClient, Box<dyn std::error::Error>> {
         let address = std::env::var(ADDRESS_ENV_VAR).expect(ADDRESS_ENV_VAR);
-        // TODO: introduce a rust_proto_new::Endpoint type, or pub-use the Tonic one.
+        // TODO: introduce a rust_proto::Endpoint type, or pub-use the Tonic one.
         Self::connect(address.clone()).await
     }
 }
diff --git a/src/rust/plugin-work-queue/src/lib.rs b/src/rust/plugin-work-queue/src/lib.rs
index c955360908..99fc229d66 100644
--- a/src/rust/plugin-work-queue/src/lib.rs
+++ b/src/rust/plugin-work-queue/src/lib.rs
@@ -3,7 +3,7 @@ use std::net::SocketAddr;
 pub mod client;
 pub mod psql_queue;
 pub mod server;
-#[cfg(feature = "new_integration_tests")]
+#[cfg(feature = "integration_tests")]
 pub mod test_utils;
 
 #[derive(clap::Parser, Clone, Debug)]
diff --git a/src/rust/plugin-work-queue/src/server.rs b/src/rust/plugin-work-queue/src/server.rs
index 6f83e245b9..7fb85be0dd 100644
--- a/src/rust/plugin-work-queue/src/server.rs
+++ b/src/rust/plugin-work-queue/src/server.rs
@@ -1,6 +1,6 @@
 use std::time::Duration;
 
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::plugin_work_queue::{
         v1beta1,
         v1beta1::{
diff --git a/src/rust/rust-proto-new/Cargo.toml b/src/rust/rust-proto-new/Cargo.toml
deleted file mode 100644
index 1603845673..0000000000
--- a/src/rust/rust-proto-new/Cargo.toml
+++ /dev/null
@@ -1,44 +0,0 @@
-[package]
-name = "rust-proto-new"
-version = "0.1.0"
-authors = ["Grapl, Inc. <sales@graplsecurity.com>"]
-edition = "2021"
-description = "Wrapper library for Grapl's protocol buffers"
-license = "MIT"
-
-[dependencies]
-bytes = "1.1"
-futures = "0.3"
-grapl-utils = { path = "../grapl-utils" }
-prost = "0.10"
-prost-types = "0.10"
-thiserror = "1.0"
-tokio = { version = "1.17", features = ["rt", "time"] }
-tokio-stream = { version = "0.1.8", features = ["net"] }
-tonic = { version = "0.7", features = ["default", "compression", "tls"] }
-tonic-health = "0.6"
-tracing = "0.1"
-uuid = { version = "0.8.2", features = ["v4"] }
-
-[build-dependencies]
-tonic-build = { version = "0.7", features = ["default", "compression"] }
-
-[dev-dependencies]
-proptest = "1.0"
-quickcheck = "1.0"
-quickcheck_macros = "1.0"
-test-context = "0.1"
-tracing-subscriber = { version = "0.3", features = ["env-filter"] }
-
-[features]
-default = ["uid-allocator-full"]
-uid-allocator = []
-uid-allocator-messages = ["uid-allocator"]
-uid-allocator-server = ["uid-allocator-messages", "uid-allocator"]
-uid-allocator-client = ["uid-allocator-messages", "uid-allocator"]
-uid-allocator-full = [
-  "uid-allocator-messages",
-  "uid-allocator-client",
-  "uid-allocator-server",
-  "uid-allocator"
-]
diff --git a/src/rust/rust-proto-new/build.rs b/src/rust/rust-proto-new/build.rs
deleted file mode 100644
index 8e9bef7293..0000000000
--- a/src/rust/rust-proto-new/build.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use std::{
-    ffi::OsStr,
-    path::Path,
-};
-
-fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let config = tonic_build::configure();
-    println!("cargo:rerun-if-changed=Cargo.toml");
-    println!("cargo:rerun-if-changed=../Cargo.lock");
-    println!("cargo:rerun-if-changed=build.rs");
-
-    change_on_dir(Path::new("../../proto/"))?;
-
-    let mut paths = Vec::new();
-    get_proto_files("../../proto/graplinc", &mut paths)?;
-
-    config
-        .build_client(true)
-        .build_server(true)
-        .compile(&paths[..], &["../../proto/".to_string()])
-        .unwrap_or_else(|e| panic!("protobuf compilation failed: {}", e));
-    Ok(())
-}
-
-fn get_proto_files(path: &str, paths: &mut Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
-    for entry in std::fs::read_dir(path)? {
-        let entry = entry?;
-        if entry.metadata()?.is_file() {
-            if let Some("proto") = entry.path().extension().and_then(OsStr::to_str) {
-                paths.push(entry.path().display().to_string());
-            }
-        } else {
-            let path = entry.path().display().to_string();
-            get_proto_files(&path, paths)?;
-        }
-    }
-    Ok(())
-}
-
-fn change_on_dir(root_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
-    let current_dir = std::env::current_dir()?;
-    for entry in std::fs::read_dir(current_dir.join(root_dir))? {
-        let entry = entry?;
-        let metadata = entry.metadata()?;
-        if metadata.is_dir() {
-            change_on_dir(&entry.path())?;
-        }
-        if metadata.is_file() {
-            let path = entry.path();
-            let path = path.canonicalize()?;
-            println!("cargo:rerun-if-changed={}", path.display());
-        }
-    }
-    Ok(())
-}
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/uid_allocator/v1beta1.rs b/src/rust/rust-proto-new/src/graplinc/grapl/api/uid_allocator/v1beta1.rs
deleted file mode 100644
index 8b13789179..0000000000
--- a/src/rust/rust-proto-new/src/graplinc/grapl/api/uid_allocator/v1beta1.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-
diff --git a/src/rust/rust-proto-new/src/lib.rs b/src/rust/rust-proto-new/src/lib.rs
deleted file mode 100644
index 3d6c373b70..0000000000
--- a/src/rust/rust-proto-new/src/lib.rs
+++ /dev/null
@@ -1,319 +0,0 @@
-use std::time::SystemTimeError;
-
-use bytes::{
-    Buf,
-    Bytes,
-};
-use prost::{
-    DecodeError,
-    EncodeError,
-};
-use thiserror::Error;
-
-pub mod protocol;
-
-pub(crate) mod server_internals;
-
-// This module hierarchy contains all the stubs generated by the protocol buffer
-// compiler. They are not to be exported by this crate's public API, instead
-// they will remain internal to this crate.
-pub(crate) mod protobufs {
-    pub(crate) mod graplinc {
-        pub(crate) mod common {
-            pub(crate) mod v1beta1 {
-                include!(concat!(env!("OUT_DIR"), "/graplinc.common.v1beta1.rs"));
-            }
-        }
-
-        pub(crate) mod grapl {
-            pub(crate) mod api {
-                pub(crate) mod event_source {
-                    pub(crate) mod v1beta1 {
-                        include!(concat!(
-                            env!("OUT_DIR"),
-                            "/graplinc.grapl.api.event_source.v1beta1.rs"
-                        ));
-                    }
-                }
-
-                pub(crate) mod graph {
-                    pub(crate) mod v1beta1 {
-                        include!(concat!(
-                            env!("OUT_DIR"),
-                            "/graplinc.grapl.api.graph.v1beta1.rs"
-                        ));
-                    }
-                }
-
-                pub(crate) mod pipeline_ingress {
-                    pub(crate) mod v1beta1 {
-                        include!(concat!(
-                            env!("OUT_DIR"),
-                            "/graplinc.grapl.api.pipeline_ingress.v1beta1.rs"
-                        ));
-                    }
-                }
-
-                pub(crate) mod plugin_bootstrap {
-                    pub(crate) mod v1beta1 {
-                        include!(concat!(
-                            env!("OUT_DIR"),
-                            "/graplinc.grapl.api.plugin_bootstrap.v1beta1.rs"
-                        ));
-                    }
-                }
-
-                pub(crate) mod plugin_registry {
-                    pub(crate) mod v1beta1 {
-                        include!(concat!(
-                            env!("OUT_DIR"),
-                            "/graplinc.grapl.api.plugin_registry.v1beta1.rs"
-                        ));
-                    }
-                }
-
-                pub(crate) mod plugin_sdk {
-                    pub(crate) mod generators {
-                        pub(crate) mod v1beta1 {
-                            include!(concat!(
-                                env!("OUT_DIR"),
-                                "/graplinc.grapl.api.plugin_sdk.generators.v1beta1.rs"
-                            ));
-                        }
-                    }
-                }
-
-                pub(crate) mod plugin_work_queue {
-                    pub(crate) mod v1beta1 {
-                        include!(concat!(
-                            env!("OUT_DIR"),
-                            "/graplinc.grapl.api.plugin_work_queue.v1beta1.rs"
-                        ));
-                    }
-                }
-
-                #[cfg(feature = "uid-allocator")]
-                pub(crate) mod uid_allocator {
-                    pub(crate) mod v1beta1 {
-                        include!(concat!(
-                            env!("OUT_DIR"),
-                            "/graplinc.grapl.api.uid_allocator.v1beta1.rs"
-                        ));
-                    }
-                }
-            }
-
-            pub(crate) mod pipeline {
-                pub(crate) mod v1beta1 {
-                    include!(concat!(
-                        env!("OUT_DIR"),
-                        "/graplinc.grapl.pipeline.v1beta1.rs"
-                    ));
-                }
-
-                pub(crate) mod v1beta2 {
-                    include!(concat!(
-                        env!("OUT_DIR"),
-                        "/graplinc.grapl.pipeline.v1beta2.rs"
-                    ));
-                }
-            }
-        }
-    }
-}
-
-// This module hierarchy closely emulates the one above, but contains our rust
-// language representations of the wire format. This module hierarchy must be
-// kept synchronized with the one above. This module hierarchy defines this
-// crate's public API.
-pub mod graplinc {
-    pub mod common {
-        pub mod v1beta1;
-    }
-
-    pub mod grapl {
-        pub mod api {
-            pub mod event_source {
-                pub mod v1beta1;
-            }
-
-            pub mod graph {
-                pub mod v1beta1;
-            }
-
-            pub mod model_plugin_deployer {
-                pub mod v1;
-            }
-
-            pub mod pipeline_ingress {
-                pub mod v1beta1;
-            }
-
-            pub mod plugin_bootstrap {
-                pub mod v1beta1;
-            }
-
-            pub mod plugin_registry {
-                pub mod v1beta1;
-                mod v1beta1_client;
-                mod v1beta1_server;
-            }
-
-            pub mod plugin_sdk {
-                pub mod generators {
-                    pub mod v1beta1;
-                }
-            }
-
-            pub mod plugin_work_queue {
-                pub mod v1beta1;
-                mod v1beta1_client;
-                mod v1beta1_server;
-            }
-
-            #[cfg(feature = "uid-allocator")]
-            pub mod uid_allocator {
-                pub mod v1beta1;
-            }
-        }
-
-        pub mod metrics {
-            pub mod v1;
-        }
-
-        pub mod pipeline {
-            pub mod v1beta1;
-            pub mod v1beta2;
-        }
-    }
-}
-
-// This one's a little funky. See https://stackoverflow.com/a/53207767. The idea
-// here is that we _do not want_ to expose the TYPE_URL as part of this crate's
-// public API. However, in order to serialize a google.protobuf.Any message we
-// require the TYPE_URL. Since prost does not support protocol buffer reflection
-// features--descriptors, crucially--we are unable to determine the TYPE_URL for
-// an arbitrary protocol buffer type at runtime. Therefore we resort to
-// hard-coding them.
-//
-// The critical invariant here is that the TYPE_URL must be identically the same
-// for any protocol buffer as that generated by the python implementation of
-// google.protobuf.any_pb2.Any#Pack as used in python-proto. We maintain this
-// invariant with round-trip regression tests which ensure every message can
-// pass unmolested through all of our serialization and deserialization code.
-//
-// Should we find the need to work with protocol buffer stubs in a third
-// language we'll add those stubs to the round-trip tests as well.
-pub(crate) mod type_url {
-    pub trait TypeUrl {
-        const TYPE_URL: &'static str;
-    }
-}
-
-#[non_exhaustive]
-#[derive(Error, Debug)]
-pub enum SerDeError {
-    #[error("failed to serialize {0}")]
-    EncodingFailed(#[from] EncodeError),
-
-    #[error("failed to deserialize {0}")]
-    DecodingFailed(#[from] DecodeError),
-
-    #[error("bad timestamp {0}")]
-    BadTimestamp(#[from] SystemTimeError),
-
-    #[error("missing message field {0}")]
-    MissingField(&'static str),
-
-    #[error("field {field_name} failed assertion {assertion}")]
-    InvalidField {
-        field_name: &'static str,
-        assertion: String,
-    },
-
-    #[error("Unknown enum variant {0}")]
-    UnknownVariant(&'static str),
-}
-
-impl From<std::convert::Infallible> for SerDeError {
-    fn from(_: std::convert::Infallible) -> Self {
-        unimplemented!()
-    }
-}
-
-pub trait SerDe: type_url::TypeUrl + Clone + std::fmt::Debug {
-    fn serialize(self) -> Result<Bytes, SerDeError>;
-
-    fn deserialize<B>(buf: B) -> Result<Self, SerDeError>
-    where
-        B: Buf,
-        Self: Sized;
-}
-
-pub(crate) mod serde_impl {
-    use bytes::{
-        Bytes,
-        BytesMut,
-    };
-    use prost::Message;
-
-    use crate::{
-        type_url,
-        SerDe,
-        SerDeError,
-    };
-
-    /// You can use this blanket implementation to automatically implement SerDe
-    /// for a type YourType defined within this crate. YourType must implement
-    /// either From<proto::YourType> or TryFrom<proto::YourType> and you must
-    /// implement either From<YourType> or TryFrom<YourType> for
-    /// proto::YourType. YourType must also implement type_url::TypeUrl. If
-    /// YourType meets these conditions you can automatically implement SerDe by
-    /// implementing the simpler trait ProtobufSerializable.
-    ///
-    /// Example usage:
-    ///
-    /// use crate::{
-    ///     protobufs::graplinc::v1beta1 as proto,
-    ///     serde_impl,
-    ///     type_url,
-    /// };
-    ///
-    /// pub struct YourType {...}
-    ///
-    /// impl type_url::TypeUrl for YourType {
-    ///     const TYPE_URL: &'static str =
-    ///         "graplsecurity.com/graplinc.v1beta1.YourType";
-    /// }
-    ///
-    /// impl serde_impl::ProtobufSerializable for YourType {
-    ///    type ProtobufMessage = proto::YourType;
-    /// }
-    pub(crate) trait ProtobufSerializable: Sized {
-        type ProtobufMessage: TryFrom<Self> + TryInto<Self> + Message + Default;
-    }
-
-    impl<T> SerDe for T
-    where
-        T: ProtobufSerializable,
-        T: type_url::TypeUrl + Clone + std::fmt::Debug,
-        SerDeError: From<<<T as ProtobufSerializable>::ProtobufMessage as TryFrom<T>>::Error>,
-        SerDeError: From<<<T as ProtobufSerializable>::ProtobufMessage as TryInto<T>>::Error>,
-    {
-        fn serialize(self: T) -> Result<Bytes, SerDeError> {
-            let proto = T::ProtobufMessage::try_from(self)?;
-            let mut buf = BytesMut::with_capacity(proto.encoded_len());
-            proto.encode(&mut buf)?;
-            Ok(buf.freeze())
-        }
-
-        fn deserialize<B>(buf: B) -> Result<T, SerDeError>
-        where
-            B: bytes::Buf,
-            Self: Sized,
-        {
-            let proto: T::ProtobufMessage = Message::decode(buf)?;
-            Ok(proto.try_into()?)
-        }
-    }
-}
diff --git a/src/rust/rust-proto/Cargo.toml b/src/rust/rust-proto/Cargo.toml
index 63c5700924..52f5ef71d1 100644
--- a/src/rust/rust-proto/Cargo.toml
+++ b/src/rust/rust-proto/Cargo.toml
@@ -1,31 +1,44 @@
 [package]
 name = "rust-proto"
-version = "0.2.10"
-authors = ["Insanitybit <insanitybit@gmail.com>"]
+version = "0.1.0"
+authors = ["Grapl, Inc. <sales@graplsecurity.com>"]
 edition = "2021"
-description = "A library for interacting with Grapl graphs"
+description = "Wrapper library for Grapl's protocol buffers"
 license = "MIT"
 
 [dependencies]
-log = "0.4.14"
-prost = "0.9.0"
-prost-types = "0.9.0"
-rand = "0.8.4"
-serde = "1.0.130"
-serde_derive = "1.0.130"
-thiserror = "1.0.30"
-tonic = "0.6"
-tracing = "0.1.29"
-uuid = { version = "0.8", features = ["v4"] }
+bytes = "1.1"
+futures = "0.3"
+grapl-utils = { path = "../grapl-utils" }
+prost = "0.10"
+prost-types = "0.10"
+thiserror = "1.0"
+tokio = { version = "1.17", features = ["rt", "time"] }
+tokio-stream = { version = "0.1.8", features = ["net"] }
+tonic = { version = "0.7", features = ["default", "compression", "tls"] }
+tonic-health = "0.6"
+tracing = "0.1"
+uuid = { version = "0.8.2", features = ["v4"] }
 
 [build-dependencies]
-tonic-build = { version = "0.6.0", features = ["prost"] }
+tonic-build = { version = "0.7", features = ["default", "compression"] }
 
 [dev-dependencies]
-quickcheck = "1.0.3"
-quickcheck_macros = "1.0.0"
-tracing-subscriber = "0.3.2"
+proptest = "1.0"
+quickcheck = "1.0"
+quickcheck_macros = "1.0"
+test-context = "0.1"
+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
 
 [features]
-default = []
-extra_assertions = []
+default = ["uid-allocator-full"]
+uid-allocator = []
+uid-allocator-messages = ["uid-allocator"]
+uid-allocator-server = ["uid-allocator-messages", "uid-allocator"]
+uid-allocator-client = ["uid-allocator-messages", "uid-allocator"]
+uid-allocator-full = [
+  "uid-allocator-messages",
+  "uid-allocator-client",
+  "uid-allocator-server",
+  "uid-allocator"
+]
diff --git a/src/rust/rust-proto/build.rs b/src/rust/rust-proto/build.rs
index c8e99593cf..8e9bef7293 100644
--- a/src/rust/rust-proto/build.rs
+++ b/src/rust/rust-proto/build.rs
@@ -4,472 +4,19 @@ use std::{
 };
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let mut config = tonic_build::configure();
+    let config = tonic_build::configure();
     println!("cargo:rerun-if-changed=Cargo.toml");
     println!("cargo:rerun-if-changed=../Cargo.lock");
     println!("cargo:rerun-if-changed=build.rs");
 
     change_on_dir(Path::new("../../proto/"))?;
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph", // TODO: all these derives should go away
-        "#[derive(serde_derive::Serialize, serde_derive::Deserialize)]",
-    );
-
-    config = config.type_attribute(".graplinc.grapl.api.graph.v1beta1", "#[derive(Eq)]");
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IncrementOnlyIntProp",
-        "#[derive(Copy, Ord, PartialOrd)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.DecrementOnlyIntProp",
-        "#[derive(Copy, Ord, PartialOrd)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ImmutableIntProp",
-        "#[derive(Copy, Ord, PartialOrd)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IncrementOnlyUintProp",
-        "#[derive(Copy, Ord, PartialOrd)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.DecrementOnlyUintProp",
-        "#[derive(Copy, Ord, PartialOrd)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ImmutableUintProp",
-        "#[derive(Copy, Ord, PartialOrd)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.MergedEdge",
-        "#[derive(Ord, PartialOrd)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Edge",
-        "#[derive(Ord, PartialOrd)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpAddress",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpPort",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.NetworkConnection",
-        "#[derive(Builder)]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpConnection",
-        "#[derive(Builder)]",
-    );
-
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpAddress",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpPort",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.NetworkConnection",
-        "#[builder(setter(into))]",
-    );
-    config = config.type_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpConnection",
-        "#[builder(setter(into))]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.created_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.deleted_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_name",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_path",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_extension",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_mime_type",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_size",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_version",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_description",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_product",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_company",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_directory",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_inode",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.file_hard_links",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.md5_hash",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.sha1_hash",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.sha256_hash",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.asset_id",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.hostname",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.File.host_ip",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.process_id",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.process_guid",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.created_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.terminated_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.process_name",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.process_command_line",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.process_integrity_level",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.operating_system",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.asset_id",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.hostname",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Process.host_ip",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.asset_id",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.hostname",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.host_ip",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.created_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.terminated_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessInboundConnection.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.asset_id",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.hostname",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.host_ip",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.created_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.terminated_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.ProcessOutboundConnection.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.asset_id",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.ip_address",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.hostname",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.mac_address",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.first_seen_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.Asset.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpAddress.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpAddress.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpAddress.first_seen_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpAddress.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpPort.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpPort.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpPort.first_seen_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpPort.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.NetworkConnection.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.NetworkConnection.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.NetworkConnection.created_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.NetworkConnection.terminated_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.NetworkConnection.last_seen_timestamp",
-        "#[builder(default)]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpConnection.node_key",
-        "#[builder(field(private))]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpConnection.node_key",
-        "#[builder(default = \"::uuid::Uuid::new_v4().to_string()\")]",
-    );
-
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpConnection.created_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpConnection.terminated_timestamp",
-        "#[builder(default)]",
-    );
-    config = config.field_attribute(
-        ".graplinc.grapl.api.graph.v1beta1.IpConnection.last_seen_timestamp",
-        "#[builder(default)]",
-    );
 
     let mut paths = Vec::new();
-    get_proto_files("../../proto", &mut paths)?;
+    get_proto_files("../../proto/graplinc", &mut paths)?;
 
     config
+        .build_client(true)
+        .build_server(true)
         .compile(&paths[..], &["../../proto/".to_string()])
         .unwrap_or_else(|e| panic!("protobuf compilation failed: {}", e));
     Ok(())
diff --git a/src/rust/rust-proto/src/graph_descriptions.rs b/src/rust/rust-proto/src/graph_descriptions.rs
deleted file mode 100644
index bdcc0791c5..0000000000
--- a/src/rust/rust-proto/src/graph_descriptions.rs
+++ /dev/null
@@ -1,1093 +0,0 @@
-pub use node_property::Property;
-use node_property::Property::{
-    DecrementOnlyInt as ProtoDecrementOnlyIntProp,
-    DecrementOnlyUint as ProtoDecrementOnlyUintProp,
-    ImmutableInt as ProtoImmutableIntProp,
-    ImmutableStr as ProtoImmutableStrProp,
-    ImmutableUint as ProtoImmutableUintProp,
-    IncrementOnlyInt as ProtoIncrementOnlyIntProp,
-    IncrementOnlyUint as ProtoIncrementOnlyUintProp,
-};
-
-pub use crate::graplinc::grapl::api::graph::v1beta1::*;
-use crate::pipeline::ServiceMessage;
-
-// A helper macro to generate `From` impl boilerplate.
-macro_rules ! impl_from_for_unit {
-    ($into_t:ty, $field:tt, $from_t:ty) => {
-        impl From<$from_t> for $into_t
-        {
-            fn from(p: $from_t) -> Self {
-                let p = p.to_owned().into();
-                Self {$field: p}
-            }
-        }
-    };
-    ($into_t:ty, $field:tt, $head:ty, $($tail:ty),*) => {
-        impl_from_for_unit!($into_t, $field, $head);
-        impl_from_for_unit!($into_t, $field, $($tail),*);
-    };
-}
-
-macro_rules! extra_assert {
-    ($x:expr) => {
-        #[cfg(feature = "extra_assertions")]
-        {
-            $x
-        }
-    };
-}
-
-impl EdgeList {
-    pub fn into_vec(self) -> Vec<Edge> {
-        let Self { edges } = self;
-        edges
-    }
-}
-
-impl ServiceMessage for GraphDescription {
-    const TYPE_NAME: &'static str = "graplinc.grapl.api.graph.v1beta1.GraphDescription";
-}
-
-impl GraphDescription {
-    pub fn new() -> Self {
-        Self {
-            nodes: Default::default(),
-            edges: Default::default(),
-        }
-    }
-
-    pub fn add_node(&mut self, node: impl Into<NodeDescription>) {
-        let node = node.into();
-        match self.nodes.get_mut(&node.node_key) {
-            Some(n) => n.merge(&node),
-            None => {
-                self.nodes.insert(node.clone_node_key(), node);
-            }
-        };
-    }
-
-    pub fn add_edge(
-        &mut self,
-        edge_name: impl Into<String>,
-        from_node_key: impl Into<String>,
-        to_node_key: impl Into<String>,
-    ) {
-        let from_node_key = from_node_key.into();
-        let to_node_key = to_node_key.into();
-        let edge_name = edge_name.into();
-
-        assert_ne!(from_node_key, to_node_key);
-
-        let edge = Edge {
-            from_node_key: from_node_key.clone(),
-            to_node_key,
-            edge_name,
-        };
-
-        let edge_list: &mut Vec<Edge> = &mut self
-            .edges
-            .entry(from_node_key)
-            .or_insert_with(|| EdgeList {
-                edges: Vec::with_capacity(1),
-            })
-            .edges;
-        edge_list.push(edge);
-    }
-
-    pub fn merge(&mut self, other: &Self) {
-        for (node_key, other_node) in other.nodes.iter() {
-            match self.nodes.get_mut(node_key) {
-                Some(n) => n.merge(other_node),
-                None => {
-                    self.nodes.insert(node_key.clone(), other_node.clone());
-                }
-            };
-        }
-
-        for edge_list in other.edges.values() {
-            for edge in edge_list.edges.iter() {
-                self.add_edge(
-                    edge.edge_name.clone(),
-                    edge.from_node_key.clone(),
-                    edge.to_node_key.clone(),
-                );
-            }
-        }
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.nodes.is_empty() && self.edges.is_empty()
-    }
-}
-
-impl IdentifiedGraph {
-    pub fn new() -> Self {
-        Self {
-            nodes: Default::default(),
-            edges: Default::default(),
-        }
-    }
-
-    pub fn add_node(&mut self, node: IdentifiedNode) {
-        match self.nodes.get_mut(&node.node_key) {
-            Some(n) => n.merge(&node),
-            None => {
-                self.nodes.insert(node.clone_node_key(), node);
-            }
-        };
-    }
-
-    pub fn add_edge(
-        &mut self,
-        edge_name: impl Into<String>,
-        from_node_key: impl Into<String>,
-        to_node_key: impl Into<String>,
-    ) {
-        let from_node_key = from_node_key.into();
-        let to_node_key = to_node_key.into();
-        assert_ne!(from_node_key, to_node_key);
-
-        let edge_name = edge_name.into();
-        let edge = Edge {
-            from_node_key: from_node_key.clone(),
-            to_node_key,
-            edge_name,
-        };
-
-        let edge_list: &mut Vec<Edge> = &mut self
-            .edges
-            .entry(from_node_key)
-            .or_insert_with(|| EdgeList {
-                edges: Vec::with_capacity(1),
-            })
-            .edges;
-        edge_list.push(edge);
-    }
-
-    pub fn merge(&mut self, other: &Self) {
-        for (node_key, other_node) in other.nodes.iter() {
-            match self.nodes.get_mut(node_key) {
-                Some(n) => n.merge(other_node),
-                None => {
-                    self.nodes.insert(node_key.clone(), other_node.clone());
-                }
-            };
-        }
-
-        for edge_list in other.edges.values() {
-            for edge in edge_list.edges.iter() {
-                self.add_edge(
-                    edge.edge_name.clone(),
-                    edge.from_node_key.clone(),
-                    edge.to_node_key.clone(),
-                );
-            }
-        }
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.nodes.is_empty() && self.edges.is_empty()
-    }
-}
-
-impl ServiceMessage for IdentifiedGraph {
-    const TYPE_NAME: &'static str = "graplinc.grapl.api.graph.v1beta1.IdentifiedGraph";
-}
-
-impl MergedGraph {
-    pub fn new() -> Self {
-        Self {
-            nodes: Default::default(),
-            edges: Default::default(),
-        }
-    }
-
-    pub fn add_node(&mut self, node: MergedNode) {
-        match self.nodes.get_mut(&node.node_key) {
-            Some(n) => n.merge(&node),
-            None => {
-                self.nodes.insert(node.clone_node_key(), node);
-            }
-        };
-    }
-
-    pub fn add_merged_edge(&mut self, edge: MergedEdge) {
-        let from_node_key = edge.from_node_key.clone();
-        let edge_list: &mut Vec<MergedEdge> = &mut self
-            .edges
-            .entry(from_node_key)
-            .or_insert_with(|| MergedEdgeList {
-                edges: Vec::with_capacity(1),
-            })
-            .edges;
-        edge_list.push(edge);
-    }
-
-    pub fn add_edge(
-        &mut self,
-        edge_name: impl Into<String>,
-        from_node_key: impl Into<String>,
-        from_uid: impl Into<String>,
-        to_node_key: impl Into<String>,
-        to_uid: impl Into<String>,
-    ) {
-        let edge_name = edge_name.into();
-        let from_node_key = from_node_key.into();
-        let from_uid = from_uid.into();
-        let to_node_key = to_node_key.into();
-        let to_uid = to_uid.into();
-        assert_ne!(from_node_key, to_node_key);
-        assert_ne!(from_uid, to_uid);
-        let edge = MergedEdge {
-            from_node_key: from_node_key.clone(),
-            from_uid,
-            to_node_key,
-            to_uid,
-            edge_name,
-        };
-
-        let edge_list: &mut Vec<MergedEdge> = &mut self
-            .edges
-            .entry(from_node_key)
-            .or_insert_with(|| MergedEdgeList {
-                edges: Vec::with_capacity(1),
-            })
-            .edges;
-        edge_list.push(edge);
-    }
-
-    pub fn merge(&mut self, other: &Self) {
-        for (node_key, other_node) in other.nodes.iter() {
-            match self.nodes.get_mut(node_key) {
-                Some(n) => n.merge(other_node),
-                None => {
-                    self.nodes.insert(node_key.clone(), other_node.clone());
-                }
-            };
-        }
-
-        for edge_list in other.edges.values() {
-            for edge in edge_list.edges.iter() {
-                self.add_edge(
-                    edge.edge_name.clone(),
-                    edge.from_node_key.clone(),
-                    edge.from_uid.clone(),
-                    edge.to_node_key.clone(),
-                    edge.to_uid.clone(),
-                );
-            }
-        }
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.nodes.is_empty() && self.edges.is_empty()
-    }
-}
-
-impl NodeDescription {
-    pub fn merge(&mut self, other: &Self) {
-        extra_assert!(debug_assert_eq!(self.node_type, other.node_type));
-        extra_assert!(debug_assert_eq!(self.node_key, other.node_key));
-        for (prop_name, prop_value) in other.properties.iter() {
-            match self.properties.get_mut(prop_name) {
-                Some(self_prop) => self_prop.merge(prop_value),
-                None => {
-                    self.properties
-                        .insert(prop_name.clone(), prop_value.clone());
-                }
-            }
-        }
-    }
-    pub fn get_node_key(&self) -> &str {
-        self.node_key.as_str()
-    }
-
-    pub fn clone_node_key(&self) -> String {
-        self.node_key.clone()
-    }
-}
-
-impl ServiceMessage for MergedGraph {
-    const TYPE_NAME: &'static str = "graplinc.grapl.api.graph.v1beta1.MergedGraph";
-}
-
-impl IdentifiedNode {
-    pub fn merge(&mut self, other: &Self) {
-        extra_assert!(debug_assert_eq!(self.node_type, other.node_type));
-        extra_assert!(debug_assert_eq!(self.node_key, other.node_key));
-        for (prop_name, prop_value) in other.properties.iter() {
-            match self.properties.get_mut(prop_name) {
-                Some(self_prop) => self_prop.merge(prop_value),
-                None => {
-                    self.properties
-                        .insert(prop_name.clone(), prop_value.clone());
-                }
-            }
-        }
-    }
-
-    pub fn get_cache_identities_for_predicates(&self) -> Vec<Vec<u8>> {
-        let mut predicate_cache_identities = Vec::with_capacity(self.properties.len());
-
-        for (key, prop) in &self.properties {
-            let prop_value = prop
-                .property
-                .as_ref()
-                .map(Property::to_string)
-                .unwrap_or_else(|| panic!("Invalid property on DynamicNode: {}", self.node_key));
-
-            predicate_cache_identities.push(format!("{}:{}:{}", &self.node_key, key, prop_value));
-        }
-
-        predicate_cache_identities
-            .into_iter()
-            .map(|item| item.into_bytes())
-            .collect()
-    }
-}
-
-impl NodeProperty {
-    pub fn merge(&mut self, other: &Self) {
-        match (self.property.as_mut(), other.property.as_ref()) {
-            (Some(p), Some(op)) => {
-                p.merge_property(op);
-            }
-            (None, Some(op)) => {
-                debug_assert!(false, "Unhandled property merge, self is None: {:?}", op);
-                tracing::warn!("Unhandled property merge, self is None: {:?}", op);
-            }
-            (Some(p), None) => {
-                debug_assert!(false, "Unhandled property merge, other is None: {:?}", p);
-                tracing::warn!("Unhandled property merge, other is None: {:?}", p);
-            }
-            (None, None) => {
-                debug_assert!(false, "Unhandled property merge, both properties are None");
-                tracing::warn!(message = "Unhandled property merge, both properties are None");
-            }
-        }
-    }
-}
-
-impl Property {
-    pub fn merge_property(&mut self, other: &Self) {
-        match (self, other) {
-            (
-                ProtoIncrementOnlyUintProp(ref mut self_prop),
-                ProtoIncrementOnlyUintProp(ref other_prop),
-            ) => self_prop.merge_property(other_prop),
-            (ProtoImmutableUintProp(ref mut self_prop), ProtoImmutableUintProp(ref other_prop)) => {
-                self_prop.merge_property(other_prop)
-            }
-            (
-                ProtoDecrementOnlyUintProp(ref mut self_prop),
-                ProtoDecrementOnlyUintProp(ref other_prop),
-            ) => self_prop.merge_property(other_prop),
-            (
-                ProtoDecrementOnlyIntProp(ref mut self_prop),
-                ProtoDecrementOnlyIntProp(ref other_prop),
-            ) => self_prop.merge_property(other_prop),
-            (
-                ProtoIncrementOnlyIntProp(ref mut self_prop),
-                ProtoIncrementOnlyIntProp(ref other_prop),
-            ) => self_prop.merge_property(other_prop),
-            (ProtoImmutableIntProp(ref mut self_prop), ProtoImmutableIntProp(ref other_prop)) => {
-                self_prop.merge_property(other_prop)
-            }
-            (ProtoImmutableStrProp(ref mut self_prop), ProtoImmutableStrProp(ref other_prop)) => {
-                self_prop.merge_property(other_prop)
-            }
-            // technically we could improve type safety here by exhausting the combinations,
-            // but I'm not going to type that all out right now
-            (p, op) => {
-                // Currently we don't guarantee that randomly generated nodes will have consistent
-                // property types when they share a property name
-                extra_assert!(debug_assert!(
-                    false,
-                    "Invalid property merge: {:?} {:?}",
-                    p, op
-                ));
-                tracing::warn!("Invalid property merge: {:?} {:?}", p, op);
-            }
-        }
-    }
-}
-
-impl From<Static> for IdStrategy {
-    fn from(strategy: Static) -> IdStrategy {
-        IdStrategy {
-            strategy: Some(id_strategy::Strategy::Static(strategy)),
-        }
-    }
-}
-
-impl From<Session> for IdStrategy {
-    fn from(strategy: Session) -> IdStrategy {
-        IdStrategy {
-            strategy: Some(id_strategy::Strategy::Session(strategy)),
-        }
-    }
-}
-impl std::string::ToString for Property {
-    fn to_string(&self) -> String {
-        match self {
-            ProtoIncrementOnlyUintProp(increment_only_uint_prop) => {
-                increment_only_uint_prop.to_string()
-            }
-            ProtoImmutableUintProp(immutable_uint_prop) => immutable_uint_prop.to_string(),
-            ProtoDecrementOnlyUintProp(decrement_only_uint_prop) => {
-                decrement_only_uint_prop.to_string()
-            }
-            ProtoDecrementOnlyIntProp(decrement_only_int_prop) => {
-                decrement_only_int_prop.to_string()
-            }
-            ProtoIncrementOnlyIntProp(increment_only_int_prop) => {
-                increment_only_int_prop.to_string()
-            }
-            ProtoImmutableIntProp(immutable_int_prop) => immutable_int_prop.to_string(),
-            ProtoImmutableStrProp(immutable_str_prop) => immutable_str_prop.to_string(),
-        }
-    }
-}
-
-impl IncrementOnlyUintProp {
-    pub fn as_inner(&self) -> u64 {
-        self.prop
-    }
-    pub fn merge_property(&mut self, other_prop: &Self) {
-        tracing::trace!(message="IncrementOnlyUintProp merge", self_prop=?self, other_prop=?other_prop);
-        self.prop = std::cmp::max(self.prop, other_prop.prop);
-    }
-}
-impl ImmutableUintProp {
-    pub fn as_inner(&self) -> u64 {
-        self.prop
-    }
-    pub fn merge_property(&mut self, other_prop: &Self) {
-        tracing::trace!(message="ImmutableUintProp merge", self_prop=?self, other_prop=?other_prop);
-        extra_assert! {debug_assert_eq!(*self, *other_prop)}
-    }
-}
-impl DecrementOnlyUintProp {
-    pub fn as_inner(&self) -> u64 {
-        self.prop
-    }
-    pub fn merge_property(&mut self, other_prop: &Self) {
-        self.prop = std::cmp::min(self.prop, other_prop.prop);
-    }
-}
-impl DecrementOnlyIntProp {
-    pub fn as_inner(&self) -> i64 {
-        self.prop
-    }
-    pub fn merge_property(&mut self, other_prop: &Self) {
-        self.prop = std::cmp::min(self.prop, other_prop.prop);
-    }
-}
-impl IncrementOnlyIntProp {
-    pub fn as_inner(&self) -> i64 {
-        self.prop
-    }
-
-    pub fn merge_property(&mut self, other_prop: &Self) {
-        tracing::trace!(message="IncrementOnlyIntProp merge", self_prop=?self, other_prop=?other_prop);
-        self.prop = std::cmp::max(self.prop, other_prop.prop);
-    }
-}
-impl ImmutableIntProp {
-    pub fn as_inner(&self) -> i64 {
-        self.prop
-    }
-    pub fn merge_property(&mut self, other_prop: &Self) {
-        tracing::trace!(message="ImmutableIntProp merge", self_prop=?self, other_prop=?other_prop);
-        extra_assert!(debug_assert_eq!(*self, *other_prop));
-    }
-}
-impl ImmutableStrProp {
-    pub fn as_inner(&self) -> &str {
-        self.prop.as_str()
-    }
-
-    pub fn merge_property(&mut self, other_prop: &Self) {
-        tracing::trace!(message="ImmutableStrProp merge", self_prop=?self, other_prop=?other_prop);
-        extra_assert!(debug_assert_eq!(*self, *other_prop));
-    }
-}
-
-impl std::string::ToString for IncrementOnlyUintProp {
-    fn to_string(&self) -> String {
-        self.prop.to_string()
-    }
-}
-
-impl std::string::ToString for ImmutableUintProp {
-    fn to_string(&self) -> String {
-        self.prop.to_string()
-    }
-}
-
-impl std::string::ToString for DecrementOnlyUintProp {
-    fn to_string(&self) -> String {
-        self.prop.to_string()
-    }
-}
-
-impl std::string::ToString for DecrementOnlyIntProp {
-    fn to_string(&self) -> String {
-        self.prop.to_string()
-    }
-}
-
-impl std::string::ToString for IncrementOnlyIntProp {
-    fn to_string(&self) -> String {
-        self.prop.to_string()
-    }
-}
-
-impl std::string::ToString for ImmutableIntProp {
-    fn to_string(&self) -> String {
-        self.prop.to_string()
-    }
-}
-
-impl std::string::ToString for ImmutableStrProp {
-    fn to_string(&self) -> String {
-        self.prop.to_string()
-    }
-}
-
-impl std::string::ToString for NodeProperty {
-    fn to_string(&self) -> String {
-        match &self.property {
-            Some(node_property::Property::IncrementOnlyUint(increment_only_uint_prop)) => {
-                increment_only_uint_prop.to_string()
-            }
-            Some(node_property::Property::ImmutableUint(immutable_uint_prop)) => {
-                immutable_uint_prop.to_string()
-            }
-            Some(node_property::Property::DecrementOnlyUint(decrement_only_uint_prop)) => {
-                decrement_only_uint_prop.to_string()
-            }
-            Some(node_property::Property::DecrementOnlyInt(decrement_only_int_prop)) => {
-                decrement_only_int_prop.to_string()
-            }
-            Some(node_property::Property::IncrementOnlyInt(increment_only_int_prop)) => {
-                increment_only_int_prop.to_string()
-            }
-            Some(node_property::Property::ImmutableInt(immutable_int_prop)) => {
-                immutable_int_prop.to_string()
-            }
-            Some(node_property::Property::ImmutableStr(immutable_str_prop)) => {
-                immutable_str_prop.to_string()
-            }
-            None => panic!("Invalid property : {:?}", self),
-        }
-    }
-}
-
-impl NodeDescription {
-    pub fn get_property(&self, name: impl AsRef<str>) -> Option<&NodeProperty> {
-        self.properties.get(name.as_ref())
-    }
-
-    pub fn set_property(&mut self, name: impl Into<String>, value: impl Into<NodeProperty>) {
-        self.properties.insert(name.into(), value.into());
-    }
-
-    pub fn set_key(&mut self, key: String) {
-        self.node_key = key;
-    }
-}
-
-impl<T> From<T> for NodeProperty
-where
-    T: Into<Property>,
-{
-    fn from(t: T) -> Self {
-        NodeProperty {
-            property: Some(t.into()),
-        }
-    }
-}
-
-impl From<NodeDescription> for IdentifiedNode {
-    fn from(n: NodeDescription) -> Self {
-        IdentifiedNode {
-            properties: n.properties,
-            node_key: n.node_key,
-            node_type: n.node_type,
-        }
-    }
-}
-
-impl MergedNode {
-    pub fn from(n: IdentifiedNode, uid: u64) -> Self {
-        Self {
-            uid,
-            properties: n.properties,
-            node_key: n.node_key,
-            node_type: n.node_type,
-        }
-    }
-
-    pub fn merge(&mut self, other: &Self) {
-        extra_assert!(debug_assert_eq!(self.node_type, other.node_type));
-        extra_assert!(debug_assert_eq!(self.node_key, other.node_key));
-        for (prop_name, prop_value) in other.properties.iter() {
-            match self.properties.get_mut(prop_name) {
-                Some(self_prop) => self_prop.merge(prop_value),
-                None => {
-                    self.properties
-                        .insert(prop_name.clone(), prop_value.clone());
-                }
-            }
-        }
-    }
-
-    pub fn get_node_key(&self) -> &str {
-        self.node_key.as_str()
-    }
-
-    pub fn clone_node_key(&self) -> String {
-        self.node_key.clone()
-    }
-}
-
-impl IdentifiedNode {
-    pub fn into(self, uid: u64) -> MergedNode {
-        MergedNode {
-            uid,
-            properties: self.properties,
-            node_key: self.node_key,
-            node_type: self.node_type,
-        }
-    }
-    pub fn get_node_key(&self) -> &str {
-        self.node_key.as_str()
-    }
-
-    pub fn clone_node_key(&self) -> String {
-        self.node_key.clone()
-    }
-}
-
-// These are helper types that let us easily distinguish between variants of the Property type
-
-impl_from_for_unit!(
-    ImmutableUintProp,
-    prop,
-    u64,
-    u32,
-    u16,
-    u8,
-    &u64,
-    &u32,
-    &u16,
-    &u8
-);
-impl_from_for_unit!(
-    IncrementOnlyUintProp,
-    prop,
-    u64,
-    u32,
-    u16,
-    u8,
-    &u64,
-    &u32,
-    &u16,
-    &u8
-);
-impl_from_for_unit!(
-    DecrementOnlyUintProp,
-    prop,
-    u64,
-    u32,
-    u16,
-    u8,
-    &u64,
-    &u32,
-    &u16,
-    &u8
-);
-impl_from_for_unit!(
-    ImmutableIntProp,
-    prop,
-    i64,
-    i32,
-    i16,
-    i8,
-    &i64,
-    &i32,
-    &i16,
-    &i8
-);
-impl_from_for_unit!(
-    IncrementOnlyIntProp,
-    prop,
-    i64,
-    i32,
-    i16,
-    i8,
-    &i64,
-    &i32,
-    &i16,
-    &i8
-);
-impl_from_for_unit!(
-    DecrementOnlyIntProp,
-    prop,
-    i64,
-    i32,
-    i16,
-    i8,
-    &i64,
-    &i32,
-    &i16,
-    &i8
-);
-impl_from_for_unit!(
-    ImmutableStrProp,
-    prop,
-    String,
-    &String,
-    &str,
-    &std::borrow::Cow<'_, str>
-);
-
-impl From<ImmutableUintProp> for Property {
-    fn from(p: ImmutableUintProp) -> Self {
-        Self::ImmutableUint(p)
-    }
-}
-impl From<IncrementOnlyUintProp> for Property {
-    fn from(p: IncrementOnlyUintProp) -> Self {
-        Self::IncrementOnlyUint(p)
-    }
-}
-impl From<DecrementOnlyUintProp> for Property {
-    fn from(p: DecrementOnlyUintProp) -> Self {
-        Self::DecrementOnlyUint(p)
-    }
-}
-impl From<ImmutableIntProp> for Property {
-    fn from(p: ImmutableIntProp) -> Self {
-        Self::ImmutableInt(p)
-    }
-}
-impl From<IncrementOnlyIntProp> for Property {
-    fn from(p: IncrementOnlyIntProp) -> Self {
-        Self::IncrementOnlyInt(p)
-    }
-}
-impl From<DecrementOnlyIntProp> for Property {
-    fn from(p: DecrementOnlyIntProp) -> Self {
-        Self::DecrementOnlyInt(p)
-    }
-}
-impl From<ImmutableStrProp> for Property {
-    fn from(p: ImmutableStrProp) -> Self {
-        Self::ImmutableStr(p)
-    }
-}
-
-impl NodeProperty {
-    pub fn as_increment_only_uint(&self) -> Option<IncrementOnlyUintProp> {
-        match self.property {
-            Some(ProtoIncrementOnlyUintProp(ref prop)) => Some(*prop),
-            _ => None,
-        }
-    }
-
-    pub fn as_immutable_uint(&self) -> Option<ImmutableUintProp> {
-        match self.property {
-            Some(ProtoImmutableUintProp(ref prop)) => Some(*prop),
-            _ => None,
-        }
-    }
-
-    pub fn as_decrement_only_uint(&self) -> Option<DecrementOnlyUintProp> {
-        match self.property {
-            Some(ProtoDecrementOnlyUintProp(ref prop)) => Some(*prop),
-            _ => None,
-        }
-    }
-
-    pub fn as_decrement_only_int(&self) -> Option<DecrementOnlyIntProp> {
-        match self.property {
-            Some(ProtoDecrementOnlyIntProp(ref prop)) => Some(*prop),
-            _ => None,
-        }
-    }
-
-    pub fn as_increment_only_int(&self) -> Option<IncrementOnlyIntProp> {
-        match self.property {
-            Some(ProtoIncrementOnlyIntProp(ref prop)) => Some(*prop),
-            _ => None,
-        }
-    }
-
-    pub fn as_immutable_int(&self) -> Option<ImmutableIntProp> {
-        match self.property {
-            Some(ProtoImmutableIntProp(ref prop)) => Some(*prop),
-            _ => None,
-        }
-    }
-
-    pub fn as_immutable_str(&self) -> Option<&ImmutableStrProp> {
-        match self.property {
-            Some(ProtoImmutableStrProp(ref prop)) => Some(prop),
-            _ => None,
-        }
-    }
-}
-
-#[cfg(test)]
-pub mod test {
-    use std::{
-        collections::HashMap,
-        hash::Hasher,
-    };
-
-    #[cfg(not(feature = "fuzzing"))]
-    use quickcheck::{
-        Arbitrary,
-        Gen,
-    };
-    use quickcheck_macros::quickcheck;
-
-    use super::*;
-
-    impl Arbitrary for IncrementOnlyIntProp {
-        fn arbitrary(g: &mut Gen) -> Self {
-            Self {
-                prop: i64::arbitrary(g),
-            }
-        }
-    }
-    impl Arbitrary for DecrementOnlyIntProp {
-        fn arbitrary(g: &mut Gen) -> Self {
-            Self {
-                prop: i64::arbitrary(g),
-            }
-        }
-    }
-    impl Arbitrary for ImmutableIntProp {
-        fn arbitrary(g: &mut Gen) -> Self {
-            Self {
-                prop: i64::arbitrary(g),
-            }
-        }
-    }
-    impl Arbitrary for IncrementOnlyUintProp {
-        fn arbitrary(g: &mut Gen) -> Self {
-            Self {
-                prop: u64::arbitrary(g),
-            }
-        }
-    }
-    impl Arbitrary for DecrementOnlyUintProp {
-        fn arbitrary(g: &mut Gen) -> Self {
-            Self {
-                prop: u64::arbitrary(g),
-            }
-        }
-    }
-    impl Arbitrary for ImmutableUintProp {
-        fn arbitrary(g: &mut Gen) -> Self {
-            Self {
-                prop: u64::arbitrary(g),
-            }
-        }
-    }
-    impl Arbitrary for ImmutableStrProp {
-        fn arbitrary(g: &mut Gen) -> Self {
-            Self {
-                prop: String::arbitrary(g),
-            }
-        }
-    }
-
-    impl Arbitrary for Property {
-        fn arbitrary(g: &mut Gen) -> Self {
-            let props = &[
-                Property::IncrementOnlyInt(IncrementOnlyIntProp::arbitrary(g)),
-                Property::DecrementOnlyInt(DecrementOnlyIntProp::arbitrary(g)),
-                Property::ImmutableInt(ImmutableIntProp::arbitrary(g)),
-                Property::IncrementOnlyUint(IncrementOnlyUintProp::arbitrary(g)),
-                Property::DecrementOnlyUint(DecrementOnlyUintProp::arbitrary(g)),
-                Property::ImmutableUint(ImmutableUintProp::arbitrary(g)),
-                Property::ImmutableStr(ImmutableStrProp::arbitrary(g)),
-            ];
-            g.choose(props).unwrap().clone()
-        }
-    }
-
-    impl Arbitrary for NodeProperty {
-        fn arbitrary(g: &mut Gen) -> Self {
-            NodeProperty {
-                property: Some(Property::arbitrary(g)),
-            }
-        }
-    }
-
-    fn hash(bytes: &[impl AsRef<[u8]>]) -> u64 {
-        let mut hasher = std::collections::hash_map::DefaultHasher::new();
-        for _bytes in bytes {
-            hasher.write(_bytes.as_ref());
-        }
-        hasher.finish() as u64
-    }
-
-    fn choice<T: Clone>(bytes: impl AsRef<[u8]>, choices: &[T]) -> T {
-        let mut hasher = std::collections::hash_map::DefaultHasher::new();
-        hasher.write(bytes.as_ref());
-        let choice_index = (hasher.finish() as usize) % choices.len();
-        choices[choice_index].clone()
-    }
-
-    fn choose_property(node_key: &str, property_name: &str, g: &mut Gen) -> NodeProperty {
-        let s = format!("{}{}", node_key, property_name);
-
-        let props = &[
-            Property::IncrementOnlyInt(IncrementOnlyIntProp::arbitrary(g)),
-            Property::DecrementOnlyInt(DecrementOnlyIntProp::arbitrary(g)),
-            Property::IncrementOnlyUint(IncrementOnlyUintProp::arbitrary(g)),
-            Property::DecrementOnlyUint(DecrementOnlyUintProp::arbitrary(g)),
-            Property::ImmutableInt(ImmutableIntProp::from(
-                hash(&[node_key, property_name]) as i64
-            )),
-            Property::ImmutableUint(ImmutableUintProp::from(hash(&[node_key, property_name]))),
-            Property::ImmutableStr(ImmutableStrProp::from(s)),
-        ];
-        let p: Property = choice(node_key, props);
-        p.into()
-    }
-
-    impl Arbitrary for IdentifiedNode {
-        fn arbitrary(g: &mut Gen) -> Self {
-            let node_keys = &[
-                "c413e25e-9c50-4faf-8e61-f8bfb0e0d18e".to_string(),
-                "0d5c9261-2b6e-4094-8de3-b349cb0aa310".to_string(),
-                "ed1f73df-f38d-43c0-87b0-5aff06e1f68b".to_string(),
-                "6328e956-117e-4f7f-8a5b-c56be1111f43".to_string(),
-            ];
-            let node_key = g.choose(node_keys).unwrap().clone();
-
-            let node_types = &["Process", "File", "IpAddress"];
-            let node_type = choice(&node_key, node_types);
-            let mut properties = HashMap::new();
-            let property_names: Vec<String> = Vec::arbitrary(g);
-            for property_name in property_names {
-                let property = choose_property(&node_key, &property_name, g);
-                properties.insert(property_name.to_owned(), property);
-            }
-            IdentifiedNode {
-                node_key: node_key.to_owned(),
-                node_type: node_type.to_owned(),
-                properties,
-            }
-        }
-    }
-
-    fn init_test_env() {
-        let subscriber = ::tracing_subscriber::FmtSubscriber::builder()
-            .with_env_filter(::tracing_subscriber::EnvFilter::from_default_env())
-            .finish();
-        let _ = ::tracing::subscriber::set_global_default(subscriber);
-    }
-
-    // These tests mostly target ensuring that our merge functions are commutative and idempotent
-    // That said - immutable data is *not* commutative. Therefor, assertions around commutativity
-    // are disabled for for tests on immutable data via the "extra_assertions" feature
-
-    #[cfg(not(feature = "extra_assertions"))]
-    #[quickcheck]
-    fn test_merge_str(x: ImmutableStrProp, y: ImmutableStrProp) {
-        init_test_env();
-        let original = x;
-        let mut x = original.clone();
-        x.merge_property(&y);
-        assert_eq!(original, x);
-    }
-
-    #[cfg(not(feature = "extra_assertions"))]
-    #[quickcheck]
-    fn test_merge_immutable_int(mut x: ImmutableIntProp, y: ImmutableIntProp) {
-        init_test_env();
-        let original = x.clone();
-        x.merge_property(&y);
-        assert_eq!(x, original);
-    }
-
-    #[cfg(not(feature = "extra_assertions"))]
-    #[quickcheck]
-    fn test_merge_immutable_uint(mut x: ImmutableUintProp, y: ImmutableUintProp) {
-        init_test_env();
-        let original = x.clone();
-        x.merge_property(&y);
-        assert_eq!(x, original);
-    }
-
-    #[quickcheck]
-    fn test_merge_uint_max(mut x: IncrementOnlyUintProp, y: IncrementOnlyUintProp) {
-        init_test_env();
-        x.merge_property(&y);
-        assert_eq!(x, std::cmp::max(x, y));
-    }
-
-    #[quickcheck]
-    fn test_merge_int_max(mut x: IncrementOnlyIntProp, y: IncrementOnlyIntProp) {
-        init_test_env();
-        x.merge_property(&y);
-        assert_eq!(x, std::cmp::max(x, y));
-    }
-
-    #[quickcheck]
-    fn test_merge_uint_min(mut x: DecrementOnlyUintProp, y: DecrementOnlyUintProp) {
-        init_test_env();
-        x.merge_property(&y);
-        assert_eq!(x, std::cmp::min(x, y));
-    }
-
-    #[quickcheck]
-    fn test_merge_int_min(mut x: DecrementOnlyIntProp, y: DecrementOnlyIntProp) {
-        init_test_env();
-        x.merge_property(&y);
-        assert_eq!(x, std::cmp::min(x, y));
-    }
-
-    #[quickcheck]
-    fn test_merge_incr_uint_commutative(mut properties: Vec<IncrementOnlyUintProp>) {
-        init_test_env();
-        if properties.is_empty() {
-            return;
-        }
-        properties.sort_unstable();
-        let max_value = properties.iter().max().unwrap().to_owned();
-        let mut first_x = properties[0].clone();
-        for property in properties.iter() {
-            first_x.merge_property(property)
-        }
-
-        let properties: Vec<_> = properties.into_iter().rev().collect();
-        let mut first_y = properties[0].clone();
-        for property in properties.iter() {
-            first_y.merge_property(property)
-        }
-        assert_eq!(first_x, first_y);
-        assert_eq!(first_x, max_value);
-    }
-
-    #[quickcheck]
-    fn test_merge_identified_node(mut node_0: IdentifiedNode, node_1: IdentifiedNode) {
-        if node_0.node_key != node_1.node_key {
-            return;
-        }
-        // let original = node_0.clone();
-        node_0.merge(&node_1);
-
-        // for (_o_pred_name, o_pred_val) in original.iter() {
-        //     let mut copy = o_pred_val.clone();
-        // }
-    }
-}
diff --git a/src/rust/rust-proto-new/src/graplinc/common/v1beta1.rs b/src/rust/rust-proto/src/graplinc/common/v1beta1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/common/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/common/v1beta1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/event_source/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/event_source/v1beta1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/event_source/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/event_source/v1beta1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/event_source/v1beta1/client.rs b/src/rust/rust-proto/src/graplinc/grapl/api/event_source/v1beta1/client.rs
similarity index 92%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/event_source/v1beta1/client.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/event_source/v1beta1/client.rs
index f8ab81bd80..3a36883a7d 100644
--- a/src/rust/rust-proto-new/src/graplinc/grapl/api/event_source/v1beta1/client.rs
+++ b/src/rust/rust-proto/src/graplinc/grapl/api/event_source/v1beta1/client.rs
@@ -1,11 +1,11 @@
 use std::fmt::Debug;
 
-use event_source_service_client::EventSourceServiceClient as EventSourceServiceClientProto;
-
-pub use crate::protobufs::graplinc::grapl::api::event_source::v1beta1::event_source_service_client;
 use crate::{
     graplinc::grapl::api::event_source::v1beta1 as native,
-    protobufs::graplinc::grapl::api::event_source::v1beta1 as proto,
+    protobufs::graplinc::grapl::api::event_source::v1beta1::{
+        self as proto,
+        event_source_service_client::EventSourceServiceClient as EventSourceServiceClientProto,
+    },
     protocol::{
         healthcheck::ConfigurationError,
         service_client::NamedService,
@@ -19,10 +19,13 @@ use crate::{
 pub enum EventSourceServiceClientError {
     #[error("ServiceHealthConfigError {0}")]
     ServiceHealthConfigError(#[from] ConfigurationError),
+
     #[error(transparent)]
     TransportError(#[from] tonic::transport::Error),
+
     #[error("ErrorStatus")]
     ErrorStatus(#[from] Status),
+
     #[error("SerDeError")]
     SerDeError(#[from] SerDeError),
 }
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/event_source/v1beta1/server.rs b/src/rust/rust-proto/src/graplinc/grapl/api/event_source/v1beta1/server.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/event_source/v1beta1/server.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/event_source/v1beta1/server.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/graph/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/graph/v1beta1.rs
similarity index 94%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/graph/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/graph/v1beta1.rs
index d7b8cfece8..3e355b978c 100644
--- a/src/rust/rust-proto-new/src/graplinc/grapl/api/graph/v1beta1.rs
+++ b/src/rust/rust-proto/src/graplinc/grapl/api/graph/v1beta1.rs
@@ -6,6 +6,7 @@ use crate::{
         DecrementOnlyUintProp as DecrementOnlyUintPropProto,
         Edge as EdgeProto,
         EdgeList as EdgeListProto,
+        ExecutionHit as ExecutionHitProto,
         GraphDescription as GraphDescriptionProto,
         IdStrategy as IdStrategyProto,
         IdentifiedGraph as IdentifiedGraphProto,
@@ -15,6 +16,7 @@ use crate::{
         ImmutableUintProp as ImmutableUintPropProto,
         IncrementOnlyIntProp as IncrementOnlyIntPropProto,
         IncrementOnlyUintProp as IncrementOnlyUintPropProto,
+        Lens as LensProto,
         MergedEdge as MergedEdgeProto,
         MergedEdgeList as MergedEdgeListProto,
         MergedGraph as MergedGraphProto,
@@ -1346,6 +1348,48 @@ impl serde_impl::ProtobufSerializable for Edge {
     type ProtobufMessage = EdgeProto;
 }
 
+//
+// Lens
+//
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct Lens {
+    pub lens_type: String,
+    pub lens_name: String,
+    pub uid: Option<u64>,
+    pub score: Option<u64>,
+}
+
+impl From<LensProto> for Lens {
+    fn from(lens_proto: LensProto) -> Self {
+        Lens {
+            lens_type: lens_proto.lens_type,
+            lens_name: lens_proto.lens_name,
+            uid: lens_proto.uid,
+            score: lens_proto.score,
+        }
+    }
+}
+
+impl From<Lens> for LensProto {
+    fn from(lens: Lens) -> Self {
+        LensProto {
+            lens_type: lens.lens_type,
+            lens_name: lens.lens_name,
+            uid: lens.uid,
+            score: lens.score,
+        }
+    }
+}
+
+impl type_url::TypeUrl for Lens {
+    const TYPE_URL: &'static str = "graplsecurity.com/graplinc.grapl.api.graph.v1beta1.Lens";
+}
+
+impl serde_impl::ProtobufSerializable for Lens {
+    type ProtobufMessage = LensProto;
+}
+
 //
 // MergedEdge
 //
@@ -1430,6 +1474,87 @@ impl serde_impl::ProtobufSerializable for EdgeList {
     type ProtobufMessage = EdgeListProto;
 }
 
+//
+// ExecutionHit
+//
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct ExecutionHit {
+    pub nodes: HashMap<String, MergedNode>,
+    pub edges: HashMap<String, MergedEdgeList>,
+    pub analyzer_name: String,
+    pub risk_score: u64,
+    pub lenses: Vec<Lens>,
+    pub risky_node_keys: Vec<String>,
+}
+
+impl TryFrom<ExecutionHitProto> for ExecutionHit {
+    type Error = SerDeError;
+
+    fn try_from(execution_hit_proto: ExecutionHitProto) -> Result<Self, Self::Error> {
+        let mut nodes = HashMap::with_capacity(execution_hit_proto.nodes.len());
+        for (key, merged_node) in execution_hit_proto.nodes {
+            nodes.insert(key, MergedNode::try_from(merged_node)?);
+        }
+
+        let mut edges = HashMap::with_capacity(execution_hit_proto.edges.len());
+        for (key, merged_edge_list) in execution_hit_proto.edges {
+            edges.insert(key, MergedEdgeList::from(merged_edge_list));
+        }
+
+        let mut lenses = Vec::with_capacity(execution_hit_proto.lenses.len());
+        for lens in execution_hit_proto.lenses {
+            lenses.push(Lens::from(lens));
+        }
+
+        Ok(ExecutionHit {
+            nodes,
+            edges,
+            analyzer_name: execution_hit_proto.analyzer_name,
+            risk_score: execution_hit_proto.risk_score,
+            lenses,
+            risky_node_keys: execution_hit_proto.risky_node_keys,
+        })
+    }
+}
+
+impl From<ExecutionHit> for ExecutionHitProto {
+    fn from(execution_hit: ExecutionHit) -> Self {
+        let mut nodes = HashMap::with_capacity(execution_hit.nodes.len());
+        for (key, merged_node) in execution_hit.nodes {
+            nodes.insert(key, MergedNodeProto::from(merged_node));
+        }
+
+        let mut edges = HashMap::with_capacity(execution_hit.edges.len());
+        for (key, merged_edge_list) in execution_hit.edges {
+            edges.insert(key, MergedEdgeListProto::from(merged_edge_list));
+        }
+
+        let mut lenses = Vec::with_capacity(execution_hit.lenses.len());
+        for lens in execution_hit.lenses {
+            lenses.push(LensProto::from(lens));
+        }
+
+        ExecutionHitProto {
+            nodes,
+            edges,
+            analyzer_name: execution_hit.analyzer_name,
+            risk_score: execution_hit.risk_score,
+            lenses,
+            risky_node_keys: execution_hit.risky_node_keys,
+        }
+    }
+}
+
+impl type_url::TypeUrl for ExecutionHit {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.graph.v1beta1.ExecutionHit";
+}
+
+impl serde_impl::ProtobufSerializable for ExecutionHit {
+    type ProtobufMessage = ExecutionHitProto;
+}
+
 //
 // MergedEdgeList
 //
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/model_plugin_deployer/v1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/model_plugin_deployer/v1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/model_plugin_deployer/v1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/model_plugin_deployer/v1.rs
diff --git a/src/rust/rust-proto/src/graplinc/grapl/api/organization_management/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/organization_management/v1beta1.rs
new file mode 100644
index 0000000000..a0d27a07e8
--- /dev/null
+++ b/src/rust/rust-proto/src/graplinc/grapl/api/organization_management/v1beta1.rs
@@ -0,0 +1,498 @@
+use bytes::Bytes;
+use uuid::Uuid;
+
+use crate::{
+    protobufs::graplinc::grapl::api::organization_management::v1beta1::{
+        CreateOrganizationRequest as CreateOrganizationRequestProto,
+        CreateOrganizationResponse as CreateOrganizationResponseProto,
+        CreateUserRequest as CreateUserRequestProto,
+        CreateUserResponse as CreateUserResponseProto,
+    },
+    serde_impl,
+    type_url,
+    SerDeError,
+};
+
+//
+// CreateOrganizationRequest
+//
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct CreateOrganizationRequest {
+    pub organization_display_name: String,
+    pub admin_username: String,
+    pub admin_email: String,
+    pub admin_password: Bytes,
+    pub should_reset_password: bool,
+}
+
+impl From<CreateOrganizationRequestProto> for CreateOrganizationRequest {
+    fn from(create_organization_request_proto: CreateOrganizationRequestProto) -> Self {
+        CreateOrganizationRequest {
+            organization_display_name: create_organization_request_proto.organization_display_name,
+            admin_username: create_organization_request_proto.admin_username,
+            admin_email: create_organization_request_proto.admin_email,
+            admin_password: create_organization_request_proto.admin_password.into(),
+            should_reset_password: create_organization_request_proto.should_reset_password,
+        }
+    }
+}
+
+impl From<CreateOrganizationRequest> for CreateOrganizationRequestProto {
+    fn from(create_organization_request: CreateOrganizationRequest) -> Self {
+        CreateOrganizationRequestProto {
+            organization_display_name: create_organization_request.organization_display_name,
+            admin_username: create_organization_request.admin_username,
+            admin_email: create_organization_request.admin_email,
+            admin_password: create_organization_request.admin_password.to_vec(),
+            should_reset_password: create_organization_request.should_reset_password,
+        }
+    }
+}
+
+impl type_url::TypeUrl for CreateOrganizationRequest {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.organization_management.v1beta1.CreateOrganizationRequest";
+}
+
+impl serde_impl::ProtobufSerializable for CreateOrganizationRequest {
+    type ProtobufMessage = CreateOrganizationRequestProto;
+}
+
+//
+// CreateOrganizationResponse
+//
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct CreateOrganizationResponse {
+    pub organization_id: Uuid,
+}
+
+impl TryFrom<CreateOrganizationResponseProto> for CreateOrganizationResponse {
+    type Error = SerDeError;
+
+    fn try_from(
+        create_organization_response_proto: CreateOrganizationResponseProto,
+    ) -> Result<Self, Self::Error> {
+        match create_organization_response_proto.organization_id {
+            Some(organization_id) => Ok(CreateOrganizationResponse {
+                organization_id: organization_id.into(),
+            }),
+            None => Err(SerDeError::MissingField("organization_id")),
+        }
+    }
+}
+
+impl From<CreateOrganizationResponse> for CreateOrganizationResponseProto {
+    fn from(create_organization_response: CreateOrganizationResponse) -> Self {
+        CreateOrganizationResponseProto {
+            organization_id: Some(create_organization_response.organization_id.into()),
+        }
+    }
+}
+
+impl type_url::TypeUrl for CreateOrganizationResponse {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.organization_management.v1beta1.CreateOrganizationResponse";
+}
+
+impl serde_impl::ProtobufSerializable for CreateOrganizationResponse {
+    type ProtobufMessage = CreateOrganizationResponseProto;
+}
+
+//
+// CreateUserRequest
+//
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct CreateUserRequest {
+    pub organization_id: Uuid,
+    pub name: String,
+    pub email: String,
+    pub password: Bytes,
+}
+
+impl TryFrom<CreateUserRequestProto> for CreateUserRequest {
+    type Error = SerDeError;
+
+    fn try_from(create_user_request_proto: CreateUserRequestProto) -> Result<Self, Self::Error> {
+        match create_user_request_proto.organization_id {
+            Some(organization_id) => Ok(CreateUserRequest {
+                organization_id: organization_id.into(),
+                name: create_user_request_proto.name,
+                email: create_user_request_proto.email,
+                password: create_user_request_proto.password.into(),
+            }),
+            None => Err(SerDeError::MissingField("organization_id")),
+        }
+    }
+}
+
+impl From<CreateUserRequest> for CreateUserRequestProto {
+    fn from(create_user_request: CreateUserRequest) -> Self {
+        CreateUserRequestProto {
+            organization_id: Some(create_user_request.organization_id.into()),
+            name: create_user_request.name,
+            email: create_user_request.email,
+            password: create_user_request.password.to_vec(),
+        }
+    }
+}
+
+impl type_url::TypeUrl for CreateUserRequest {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.organization_management.v1beta1.CreateUserRequest";
+}
+
+impl serde_impl::ProtobufSerializable for CreateUserRequest {
+    type ProtobufMessage = CreateUserRequestProto;
+}
+
+//
+// CreateUserResponse
+//
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct CreateUserResponse {
+    pub user_id: Uuid,
+}
+
+impl TryFrom<CreateUserResponseProto> for CreateUserResponse {
+    type Error = SerDeError;
+
+    fn try_from(create_user_response_proto: CreateUserResponseProto) -> Result<Self, Self::Error> {
+        match create_user_response_proto.user_id {
+            Some(user_id) => Ok(CreateUserResponse {
+                user_id: user_id.into(),
+            }),
+            None => Err(SerDeError::MissingField("user_id")),
+        }
+    }
+}
+
+impl From<CreateUserResponse> for CreateUserResponseProto {
+    fn from(create_user_response: CreateUserResponse) -> Self {
+        CreateUserResponseProto {
+            user_id: Some(create_user_response.user_id.into()),
+        }
+    }
+}
+
+impl type_url::TypeUrl for CreateUserResponse {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.organization_management.v1beta1.CreateUserResponse";
+}
+
+impl serde_impl::ProtobufSerializable for CreateUserResponse {
+    type ProtobufMessage = CreateUserResponseProto;
+}
+
+//
+// client
+//
+
+pub mod client {
+    use futures::FutureExt;
+    use thiserror::Error;
+    use tokio::time::error::Elapsed;
+    use tonic::Request;
+
+    use crate::{
+        protobufs::graplinc::grapl::api::organization_management::v1beta1::organization_management_service_client::OrganizationManagementServiceClient as OrganizationManagementServiceClientProto,
+        protocol::healthcheck::HealthcheckError,
+        SerDeError,
+    };
+
+    use super::{
+        CreateOrganizationRequest,
+        CreateOrganizationResponse,
+        CreateUserRequest,
+        CreateUserResponse,
+    };
+
+    #[non_exhaustive]
+    #[derive(Debug, Error)]
+    pub enum ConfigurationError {
+        #[error("failed to connect {0}")]
+        ConnectionError(#[from] tonic::transport::Error),
+
+        #[error("healthcheck failed {0}")]
+        HealtcheckFailed(#[from] HealthcheckError),
+
+        #[error("timeout elapsed {0}")]
+        TimeoutElapsed(#[from] Elapsed),
+    }
+
+    #[non_exhaustive]
+    #[derive(Debug, Error)]
+    pub enum OrganizationManagementClientError {
+        #[error("failed to serialize/deserialize {0}")]
+        SerDeError(#[from] SerDeError),
+
+        #[error("received unfavorable gRPC status {0}")]
+        GrpcStatus(#[from] tonic::Status),
+    }
+
+    pub struct OrganizationManagementClient {
+        proto_client: OrganizationManagementServiceClientProto<tonic::transport::Channel>,
+    }
+
+    impl OrganizationManagementClient {
+        pub async fn connect<T>(endpoint: T) -> Result<Self, ConfigurationError>
+        where
+            T: std::convert::TryInto<tonic::transport::Endpoint>,
+            T::Error: std::error::Error + Send + Sync + 'static,
+        {
+            Ok(OrganizationManagementClient {
+                proto_client: OrganizationManagementServiceClientProto::connect(endpoint).await?,
+            })
+        }
+
+        pub async fn create_organization(
+            &mut self,
+            create_organization_request: CreateOrganizationRequest,
+        ) -> Result<CreateOrganizationResponse, OrganizationManagementClientError> {
+            self.proto_client
+                .create_organization(Request::new(create_organization_request.into()))
+                .map(
+                    |response| -> Result<CreateOrganizationResponse, OrganizationManagementClientError> {
+                        let inner = response?.into_inner();
+                        Ok(inner.try_into()?)
+                    }
+                )
+                .await
+        }
+
+        pub async fn create_user(
+            &mut self,
+            create_user_request: CreateUserRequest,
+        ) -> Result<CreateUserResponse, OrganizationManagementClientError> {
+            self.proto_client
+                .create_user(Request::new(create_user_request.into()))
+                .map(
+                    |response| -> Result<CreateUserResponse, OrganizationManagementClientError> {
+                        let inner = response?.into_inner();
+                        Ok(inner.try_into()?)
+                    },
+                )
+                .await
+        }
+    }
+}
+
+//
+// server
+//
+
+pub mod server {
+    use std::time::Duration;
+
+    use futures::{
+        channel::oneshot::{
+            self,
+            Receiver,
+        },
+        Future,
+        FutureExt,
+    };
+    use thiserror::Error;
+    use tokio::net::TcpListener;
+    use tokio_stream::wrappers::TcpListenerStream;
+    use tonic::transport::{
+        NamedService,
+        Server,
+    };
+
+    use super::{
+        CreateOrganizationRequest,
+        CreateOrganizationResponse,
+        CreateUserRequest,
+        CreateUserResponse,
+    };
+    use crate::{
+        protobufs::graplinc::grapl::api::organization_management::v1beta1::{
+            organization_management_service_server::{
+                OrganizationManagementService as OrganizationManagementServiceProto,
+                OrganizationManagementServiceServer as OrganizationManagementServiceServerProto,
+            },
+            CreateOrganizationRequest as CreateOrganizationRequestProto,
+            CreateOrganizationResponse as CreateOrganizationResponseProto,
+            CreateUserRequest as CreateUserRequestProto,
+            CreateUserResponse as CreateUserResponseProto,
+        },
+        protocol::{
+            healthcheck::{
+                server::init_health_service,
+                HealthcheckError,
+                HealthcheckStatus,
+            },
+            status::Status,
+        },
+        server_internals::GrpcApi,
+        SerDeError,
+    };
+
+    //
+    // protocol buffer translation layer
+    //
+
+    #[tonic::async_trait]
+    impl<T> OrganizationManagementServiceProto for GrpcApi<T>
+    where
+        T: OrganizationManagementApi + Send + Sync + 'static,
+    {
+        async fn create_organization(
+            &self,
+            request: tonic::Request<CreateOrganizationRequestProto>,
+        ) -> Result<tonic::Response<CreateOrganizationResponseProto>, tonic::Status> {
+            let proto_request = request.into_inner();
+            let native_request = proto_request.into();
+            let native_response = self
+                .api_server
+                .create_organization(native_request)
+                .await
+                .map_err(Into::into)?;
+
+            let proto_response = native_response.try_into().map_err(SerDeError::from)?;
+
+            Ok(tonic::Response::new(proto_response))
+        }
+
+        async fn create_user(
+            &self,
+            request: tonic::Request<CreateUserRequestProto>,
+        ) -> Result<tonic::Response<CreateUserResponseProto>, tonic::Status> {
+            let proto_request = request.into_inner();
+            let native_request = proto_request.try_into()?;
+            let native_response = self
+                .api_server
+                .create_user(native_request)
+                .await
+                .map_err(Into::into)?;
+
+            let proto_response = native_response.try_into().map_err(SerDeError::from)?;
+
+            Ok(tonic::Response::new(proto_response))
+        }
+    }
+
+    //
+    // public API
+    //
+
+    /// Implement this trait to define the organization management API's
+    /// business logic
+    #[tonic::async_trait]
+    pub trait OrganizationManagementApi {
+        type Error: Into<Status>;
+
+        async fn create_organization(
+            &self,
+            request: CreateOrganizationRequest,
+        ) -> Result<CreateOrganizationResponse, Self::Error>;
+
+        async fn create_user(
+            &self,
+            request: CreateUserRequest,
+        ) -> Result<CreateUserResponse, Self::Error>;
+    }
+
+    #[non_exhaustive]
+    #[derive(Debug, Error)]
+    pub enum ConfigurationError {
+        #[error("encountered tonic error {0}")]
+        TonicError(#[from] tonic::transport::Error),
+    }
+
+    /// The organization management server serves the organization management
+    /// API
+    pub struct OrganizationManagementServer<T, H, F>
+    where
+        T: OrganizationManagementApi + Send + Sync + 'static,
+        H: Fn() -> F + Send + Sync + 'static,
+        F: Future<Output = Result<HealthcheckStatus, HealthcheckError>> + Send + Sync + 'static,
+    {
+        api_server: T,
+        healthcheck: H,
+        healthcheck_polling_interval: Duration,
+        tcp_listener: TcpListener,
+        shutdown_rx: Receiver<()>,
+        service_name: &'static str,
+    }
+
+    impl<T, H, F> OrganizationManagementServer<T, H, F>
+    where
+        T: OrganizationManagementApi + Send + Sync + 'static,
+        H: Fn() -> F + Send + Sync + 'static,
+        F: Future<Output = Result<HealthcheckStatus, HealthcheckError>> + Send + Sync + 'static,
+    {
+        /// Construct a new gRPC server which will serve the given API
+        /// implementation on the given socket address. Server is constructed in
+        /// a non-running state. Call the serve() method to run the server. This
+        /// method also returns a channel you can use to trigger server
+        /// shutdown.
+        pub fn new(
+            api_server: T,
+            tcp_listener: TcpListener,
+            healthcheck: H,
+            healthcheck_polling_interval: Duration,
+        ) -> (Self, oneshot::Sender<()>) {
+            let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
+            (
+                OrganizationManagementServer {
+                    api_server,
+                    healthcheck,
+                    healthcheck_polling_interval,
+                    tcp_listener,
+                    shutdown_rx,
+                    service_name: OrganizationManagementServiceServerProto::<GrpcApi<T>>::NAME,
+                },
+                shutdown_tx,
+            )
+        }
+
+        /// returns the service name associated with this service. You will need
+        /// this value to construct a HealthcheckClient with which to query this
+        /// service's healthcheck.
+        pub fn service_name(&self) -> &'static str {
+            self.service_name
+        }
+
+        /// Run the gRPC server and serve the API on this server's socket
+        /// address. Returns a ConfigurationError if the gRPC server cannot run.
+        pub async fn serve(self) -> Result<(), ConfigurationError> {
+            let service_name = &(*self.service_name());
+            let (healthcheck_handle, health_service) =
+                init_health_service::<OrganizationManagementServiceServerProto<GrpcApi<T>>, _, _>(
+                    self.healthcheck,
+                    self.healthcheck_polling_interval,
+                )
+                .await;
+
+            Ok(Server::builder()
+                .trace_fn(move |request| {
+                    tracing::info_span!(
+                        "request_log",
+                        service_name = ?service_name,
+                        headers = ?request.headers(),
+                        method = ?request.method(),
+                        uri = %request.uri(),
+                        extensions = ?request.extensions(),
+                    )
+                })
+                .add_service(health_service)
+                .add_service(OrganizationManagementServiceServerProto::new(GrpcApi::new(
+                    self.api_server,
+                )))
+                .serve_with_incoming_shutdown(
+                    TcpListenerStream::new(self.tcp_listener),
+                    self.shutdown_rx.map(|_| ()),
+                )
+                .then(|result| async move {
+                    healthcheck_handle.abort();
+                    result
+                })
+                .await?)
+        }
+    }
+}
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/pipeline_ingress/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/pipeline_ingress/v1beta1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/pipeline_ingress/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/pipeline_ingress/v1beta1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/pipeline_ingress/v1beta1/client.rs b/src/rust/rust-proto/src/graplinc/grapl/api/pipeline_ingress/v1beta1/client.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/pipeline_ingress/v1beta1/client.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/pipeline_ingress/v1beta1/client.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/pipeline_ingress/v1beta1/server.rs b/src/rust/rust-proto/src/graplinc/grapl/api/pipeline_ingress/v1beta1/server.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/pipeline_ingress/v1beta1/server.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/pipeline_ingress/v1beta1/server.rs
diff --git a/src/rust/rust-proto/src/graplinc/grapl/api/plugin_bootstrap/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_bootstrap/v1beta1.rs
new file mode 100644
index 0000000000..a6840ccee7
--- /dev/null
+++ b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_bootstrap/v1beta1.rs
@@ -0,0 +1,418 @@
+use bytes::Bytes;
+
+use crate::{
+    protobufs::graplinc::grapl::api::plugin_bootstrap::v1beta1::{
+        ClientCertificate as ClientCertificateProto,
+        GetBootstrapRequest as GetBootstrapRequestProto,
+        GetBootstrapResponse as GetBootstrapResponseProto,
+        PluginPayload as PluginPayloadProto,
+    },
+    serde_impl,
+    type_url,
+    SerDeError,
+};
+
+//
+// ClientCertificate
+//
+
+#[derive(Clone)]
+pub struct ClientCertificate {
+    pub client_certificate: Bytes,
+}
+
+impl From<ClientCertificateProto> for ClientCertificate {
+    fn from(client_certificate_proto: ClientCertificateProto) -> Self {
+        ClientCertificate {
+            client_certificate: client_certificate_proto.client_certificate.into(),
+        }
+    }
+}
+
+impl From<ClientCertificate> for ClientCertificateProto {
+    fn from(client_certificate: ClientCertificate) -> Self {
+        ClientCertificateProto {
+            client_certificate: client_certificate.client_certificate.to_vec(),
+        }
+    }
+}
+
+impl type_url::TypeUrl for ClientCertificate {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.plugin_bootstrap.v1beta1.ClientCertificate";
+}
+
+impl serde_impl::ProtobufSerializable for ClientCertificate {
+    type ProtobufMessage = ClientCertificateProto;
+}
+
+//
+// GetBootstrapRequest
+//
+
+pub struct GetBootstrapRequest {
+    // empty
+}
+
+impl From<GetBootstrapRequestProto> for GetBootstrapRequest {
+    fn from(_get_bootstrap_request_proto: GetBootstrapRequestProto) -> Self {
+        GetBootstrapRequest {}
+    }
+}
+
+impl From<GetBootstrapRequest> for GetBootstrapRequestProto {
+    fn from(_get_bootstrap_request: GetBootstrapRequest) -> Self {
+        GetBootstrapRequestProto {}
+    }
+}
+
+impl type_url::TypeUrl for GetBootstrapRequest {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.plugin_bootstrap.v1beta1.GetBootstrapRequest";
+}
+
+impl serde_impl::ProtobufSerializable for GetBootstrapRequest {
+    type ProtobufMessage = GetBootstrapRequestProto;
+}
+
+//
+// GetBootstrapResponse
+//
+
+pub struct GetBootstrapResponse {
+    pub plugin_payload: PluginPayload,
+    pub client_certificate: ClientCertificate,
+}
+
+impl TryFrom<GetBootstrapResponseProto> for GetBootstrapResponse {
+    type Error = SerDeError;
+
+    fn try_from(
+        get_bootstrap_response_proto: GetBootstrapResponseProto,
+    ) -> Result<Self, Self::Error> {
+        let client_certificate = get_bootstrap_response_proto
+            .client_certificate
+            .ok_or(SerDeError::MissingField("client_certificate"))?;
+        let plugin_payload = get_bootstrap_response_proto
+            .plugin_payload
+            .ok_or(SerDeError::MissingField("plugin_payload"))?;
+
+        Ok(GetBootstrapResponse {
+            plugin_payload: plugin_payload.into(),
+            client_certificate: client_certificate.into(),
+        })
+    }
+}
+
+impl From<GetBootstrapResponse> for GetBootstrapResponseProto {
+    fn from(get_bootstrap_response: GetBootstrapResponse) -> Self {
+        GetBootstrapResponseProto {
+            plugin_payload: Some(get_bootstrap_response.plugin_payload.into()),
+            client_certificate: Some(get_bootstrap_response.client_certificate.into()),
+        }
+    }
+}
+
+impl type_url::TypeUrl for GetBootstrapResponse {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.plugin_bootstrap.v1beta1.GetBootstrapResponse";
+}
+
+impl serde_impl::ProtobufSerializable for GetBootstrapResponse {
+    type ProtobufMessage = GetBootstrapResponseProto;
+}
+
+//
+// PluginPayload
+//
+
+#[derive(Clone)]
+pub struct PluginPayload {
+    pub plugin_binary: Bytes,
+}
+
+impl From<PluginPayloadProto> for PluginPayload {
+    fn from(plugin_payload_proto: PluginPayloadProto) -> Self {
+        PluginPayload {
+            plugin_binary: plugin_payload_proto.plugin_binary.into(),
+        }
+    }
+}
+
+impl From<PluginPayload> for PluginPayloadProto {
+    fn from(plugin_payload: PluginPayload) -> Self {
+        PluginPayloadProto {
+            plugin_binary: plugin_payload.plugin_binary.to_vec(),
+        }
+    }
+}
+
+impl type_url::TypeUrl for PluginPayload {
+    const TYPE_URL: &'static str =
+        "graplsecurity.com/graplinc.grapl.api.plugin_bootstrap.v1beta1.PluginPayload";
+}
+
+impl serde_impl::ProtobufSerializable for PluginPayload {
+    type ProtobufMessage = PluginPayloadProto;
+}
+
+//
+// client
+//
+
+pub mod client {
+    use futures::FutureExt;
+    use thiserror::Error;
+    use tokio::time::error::Elapsed;
+    use tonic::Request;
+
+    use crate::{
+        protobufs::graplinc::grapl::api::plugin_bootstrap::v1beta1::plugin_bootstrap_service_client::PluginBootstrapServiceClient as PluginBootstrapServiceClientProto,
+        protocol::healthcheck::HealthcheckError,
+        SerDeError,
+    };
+
+    use super::{
+        GetBootstrapRequest,
+        GetBootstrapResponse,
+    };
+
+    #[non_exhaustive]
+    #[derive(Debug, Error)]
+    pub enum ConfigurationError {
+        #[error("failed to connect {0}")]
+        ConnectionError(#[from] tonic::transport::Error),
+
+        #[error("healthcheck failed {0}")]
+        HealtcheckFailed(#[from] HealthcheckError),
+
+        #[error("timeout elapsed {0}")]
+        TimeoutElapsed(#[from] Elapsed),
+    }
+
+    #[non_exhaustive]
+    #[derive(Debug, Error)]
+    pub enum PluginBootstrapClientError {
+        #[error("failed to serialize/deserialize {0}")]
+        SerDeError(#[from] SerDeError),
+
+        #[error("received unfavorable gRPC status {0}")]
+        GrpcStatus(#[from] tonic::Status),
+    }
+
+    pub struct PluginBootstrapClient {
+        proto_client: PluginBootstrapServiceClientProto<tonic::transport::Channel>,
+    }
+
+    impl PluginBootstrapClient {
+        pub async fn connect<T>(endpoint: T) -> Result<Self, ConfigurationError>
+        where
+            T: std::convert::TryInto<tonic::transport::Endpoint>,
+            T::Error: std::error::Error + Send + Sync + 'static,
+        {
+            Ok(PluginBootstrapClient {
+                proto_client: PluginBootstrapServiceClientProto::connect(endpoint).await?,
+            })
+        }
+
+        pub async fn get_bootstrap(
+            &mut self,
+            get_bootstrap_request: GetBootstrapRequest,
+        ) -> Result<GetBootstrapResponse, PluginBootstrapClientError> {
+            self.proto_client
+                .get_bootstrap(Request::new(get_bootstrap_request.into()))
+                .map(
+                    |response| -> Result<GetBootstrapResponse, PluginBootstrapClientError> {
+                        let inner = response?.into_inner();
+                        Ok(inner.try_into()?)
+                    },
+                )
+                .await
+        }
+    }
+}
+
+pub mod server {
+    use std::time::Duration;
+
+    use futures::{
+        channel::oneshot::{
+            self,
+            Receiver,
+        },
+        Future,
+        FutureExt,
+    };
+    use thiserror::Error;
+    use tokio::net::TcpListener;
+    use tokio_stream::wrappers::TcpListenerStream;
+    use tonic::transport::{
+        NamedService,
+        Server,
+    };
+
+    use super::{
+        GetBootstrapRequest,
+        GetBootstrapResponse,
+    };
+    use crate::{
+        protobufs::graplinc::grapl::api::plugin_bootstrap::v1beta1::{
+            plugin_bootstrap_service_server::{
+                PluginBootstrapService as PluginBootstrapServiceProto,
+                PluginBootstrapServiceServer as PluginBootstrapServiceServerProto,
+            },
+            GetBootstrapRequest as GetBootstrapRequestProto,
+            GetBootstrapResponse as GetBootstrapResponseProto,
+        },
+        protocol::{
+            healthcheck::{
+                server::init_health_service,
+                HealthcheckError,
+                HealthcheckStatus,
+            },
+            status::Status,
+        },
+        server_internals::GrpcApi,
+        SerDeError,
+    };
+
+    //
+    // protocol buffer translation layer
+    //
+
+    #[tonic::async_trait]
+    impl<T> PluginBootstrapServiceProto for GrpcApi<T>
+    where
+        T: PluginBootstrapApi + Send + Sync + 'static,
+    {
+        async fn get_bootstrap(
+            &self,
+            request: tonic::Request<GetBootstrapRequestProto>,
+        ) -> Result<tonic::Response<GetBootstrapResponseProto>, tonic::Status> {
+            let proto_request = request.into_inner();
+            let native_request = proto_request.into();
+            let native_response = self
+                .api_server
+                .get_bootstrap(native_request)
+                .await
+                .map_err(Into::into)?;
+
+            let proto_response = native_response.try_into().map_err(SerDeError::from)?;
+
+            Ok(tonic::Response::new(proto_response))
+        }
+    }
+
+    //
+    // public API
+    //
+
+    /// Implement this trait to define the plugin bootstrap API's business logic
+    #[tonic::async_trait]
+    pub trait PluginBootstrapApi {
+        type Error: Into<Status>;
+
+        async fn get_bootstrap(
+            &self,
+            request: GetBootstrapRequest,
+        ) -> Result<GetBootstrapResponse, Self::Error>;
+    }
+
+    #[non_exhaustive]
+    #[derive(Debug, Error)]
+    pub enum ConfigurationError {
+        #[error("encountered tonic error {0}")]
+        TonicError(#[from] tonic::transport::Error),
+    }
+
+    /// The plugin bootstrap server serves the plugin bootstrap API
+    pub struct PluginBootstrapServer<T, H, F>
+    where
+        T: PluginBootstrapApi + Send + Sync + 'static,
+        H: Fn() -> F + Send + Sync + 'static,
+        F: Future<Output = Result<HealthcheckStatus, HealthcheckError>> + Send + Sync + 'static,
+    {
+        api_server: T,
+        healthcheck: H,
+        healthcheck_polling_interval: Duration,
+        tcp_listener: TcpListener,
+        shutdown_rx: Receiver<()>,
+        service_name: &'static str,
+    }
+
+    impl<T, H, F> PluginBootstrapServer<T, H, F>
+    where
+        T: PluginBootstrapApi + Send + Sync + 'static,
+        H: Fn() -> F + Send + Sync + 'static,
+        F: Future<Output = Result<HealthcheckStatus, HealthcheckError>> + Send + Sync + 'static,
+    {
+        /// Construct a new gRPC server which will serve the given API
+        /// implementation on the given socket address. Server is constructed in
+        /// a non-running state. Call the serve() method to run the server. This
+        /// method also returns a channel you can use to trigger server
+        /// shutdown.
+        pub fn new(
+            api_server: T,
+            tcp_listener: TcpListener,
+            healthcheck: H,
+            healthcheck_polling_interval: Duration,
+        ) -> (Self, oneshot::Sender<()>) {
+            let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
+            (
+                PluginBootstrapServer {
+                    api_server,
+                    healthcheck,
+                    healthcheck_polling_interval,
+                    tcp_listener,
+                    shutdown_rx,
+                    service_name: PluginBootstrapServiceServerProto::<GrpcApi<T>>::NAME,
+                },
+                shutdown_tx,
+            )
+        }
+
+        /// returns the service name associated with this service. You will need
+        /// this value to construct a HealthcheckClient with which to query this
+        /// service's healthcheck.
+        pub fn service_name(&self) -> &'static str {
+            self.service_name
+        }
+
+        /// Run the gRPC server and serve the API on this server's socket
+        /// address. Returns a ConfigurationError if the gRPC server cannot run.
+        pub async fn serve(self) -> Result<(), ConfigurationError> {
+            let service_name = &(*self.service_name());
+            let (healthcheck_handle, health_service) =
+                init_health_service::<PluginBootstrapServiceServerProto<GrpcApi<T>>, _, _>(
+                    self.healthcheck,
+                    self.healthcheck_polling_interval,
+                )
+                .await;
+
+            Ok(Server::builder()
+                .trace_fn(move |request| {
+                    tracing::info_span!(
+                        "request_log",
+                        service_name = ?service_name,
+                        headers = ?request.headers(),
+                        method = ?request.method(),
+                        uri = %request.uri(),
+                        extensions = ?request.extensions(),
+                    )
+                })
+                .add_service(health_service)
+                .add_service(PluginBootstrapServiceServerProto::new(GrpcApi::new(
+                    self.api_server,
+                )))
+                .serve_with_incoming_shutdown(
+                    TcpListenerStream::new(self.tcp_listener),
+                    self.shutdown_rx.map(|_| ()),
+                )
+                .then(|result| async move {
+                    healthcheck_handle.abort();
+                    result
+                })
+                .await?)
+        }
+    }
+}
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_registry/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_registry/v1beta1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_registry/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_registry/v1beta1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_registry/v1beta1_client.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_registry/v1beta1_client.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_registry/v1beta1_client.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_registry/v1beta1_client.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_registry/v1beta1_server.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_registry/v1beta1_server.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_registry/v1beta1_server.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_registry/v1beta1_server.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/client.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/client.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/client.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/client.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/server.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/server.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/server.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_sdk/generators/v1beta1/server.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_work_queue/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_work_queue/v1beta1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_work_queue/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_work_queue/v1beta1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_work_queue/v1beta1_client.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_work_queue/v1beta1_client.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_work_queue/v1beta1_client.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_work_queue/v1beta1_client.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_work_queue/v1beta1_server.rs b/src/rust/rust-proto/src/graplinc/grapl/api/plugin_work_queue/v1beta1_server.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_work_queue/v1beta1_server.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/plugin_work_queue/v1beta1_server.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_bootstrap/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/api/uid_allocator/v1beta1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/api/plugin_bootstrap/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/api/uid_allocator/v1beta1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/metrics/v1.rs b/src/rust/rust-proto/src/graplinc/grapl/metrics/v1.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/metrics/v1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/metrics/v1.rs
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/pipeline/v1beta1.rs b/src/rust/rust-proto/src/graplinc/grapl/pipeline/v1beta1.rs
similarity index 77%
rename from src/rust/rust-proto-new/src/graplinc/grapl/pipeline/v1beta1.rs
rename to src/rust/rust-proto/src/graplinc/grapl/pipeline/v1beta1.rs
index b5a91e4c12..0450f757a9 100644
--- a/src/rust/rust-proto-new/src/graplinc/grapl/pipeline/v1beta1.rs
+++ b/src/rust/rust-proto/src/graplinc/grapl/pipeline/v1beta1.rs
@@ -8,7 +8,6 @@ use crate::{
         Uuid,
     },
     protobufs::graplinc::grapl::pipeline::v1beta1::{
-        Envelope as EnvelopeProto,
         Metadata as MetadataProto,
         RawLog as RawLogProto,
     },
@@ -132,53 +131,6 @@ impl serde_impl::ProtobufSerializable for Metadata {
     type ProtobufMessage = MetadataProto;
 }
 
-//
-// Envelope
-//
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Envelope {
-    pub metadata: Metadata,
-    pub inner_type: String,
-    pub inner_message: Bytes,
-}
-
-impl TryFrom<EnvelopeProto> for Envelope {
-    type Error = SerDeError;
-
-    fn try_from(envelope_proto: EnvelopeProto) -> Result<Self, Self::Error> {
-        let metadata = envelope_proto
-            .metadata
-            .ok_or(SerDeError::MissingField("metadata"))?;
-
-        Ok(Envelope {
-            metadata: metadata.try_into()?,
-            inner_type: envelope_proto.inner_type,
-            inner_message: Bytes::from(envelope_proto.inner_message),
-        })
-    }
-}
-
-impl TryFrom<Envelope> for EnvelopeProto {
-    type Error = SerDeError;
-
-    fn try_from(envelope: Envelope) -> Result<Self, Self::Error> {
-        Ok(EnvelopeProto {
-            metadata: Some(envelope.metadata.try_into()?),
-            inner_type: envelope.inner_type,
-            inner_message: envelope.inner_message.to_vec(),
-        })
-    }
-}
-
-impl type_url::TypeUrl for Envelope {
-    const TYPE_URL: &'static str = "graplsecurity.com/graplinc.grapl.pipeline.v1beta1.Envelope";
-}
-
-impl serde_impl::ProtobufSerializable for Envelope {
-    type ProtobufMessage = EnvelopeProto;
-}
-
 //
 // RawLog
 //
diff --git a/src/rust/rust-proto-new/src/graplinc/grapl/pipeline/v1beta2.rs b/src/rust/rust-proto/src/graplinc/grapl/pipeline/v1beta2.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/graplinc/grapl/pipeline/v1beta2.rs
rename to src/rust/rust-proto/src/graplinc/grapl/pipeline/v1beta2.rs
diff --git a/src/rust/rust-proto/src/lib.rs b/src/rust/rust-proto/src/lib.rs
index c410510796..bd71243601 100644
--- a/src/rust/rust-proto/src/lib.rs
+++ b/src/rust/rust-proto/src/lib.rs
@@ -1,52 +1,337 @@
-pub(crate) mod graplinc {
-    pub(crate) mod common {
-        pub(crate) mod v1beta1 {
-            include!(concat!(env!("OUT_DIR"), "/graplinc.common.v1beta1.rs"));
+use std::time::SystemTimeError;
+
+use bytes::{
+    Buf,
+    Bytes,
+};
+use prost::{
+    DecodeError,
+    EncodeError,
+};
+use thiserror::Error;
+
+pub mod protocol {
+    pub mod healthcheck;
+    pub mod service_client;
+    pub mod status;
+    pub mod tls;
+}
+
+pub(crate) mod server_internals;
+
+// This module hierarchy contains all the stubs generated by the protocol buffer
+// compiler. They are not to be exported by this crate's public API, instead
+// they will remain internal to this crate.
+pub(crate) mod protobufs {
+    pub(crate) mod graplinc {
+        pub(crate) mod common {
+            pub(crate) mod v1beta1 {
+                include!(concat!(env!("OUT_DIR"), "/graplinc.common.v1beta1.rs"));
+            }
         }
-    }
 
-    pub(crate) mod grapl {
-        pub(crate) mod api {
-            pub(crate) mod graph {
-                pub(crate) mod v1beta1 {
-                    include!(concat!(
-                        env!("OUT_DIR"),
-                        "/graplinc.grapl.api.graph.v1beta1.rs"
-                    ));
+        pub(crate) mod grapl {
+            pub(crate) mod api {
+                pub(crate) mod event_source {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.event_source.v1beta1.rs"
+                        ));
+                    }
+                }
+
+                pub(crate) mod graph {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.graph.v1beta1.rs"
+                        ));
+                    }
+                }
+
+                pub(crate) mod organization_management {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.organization_management.v1beta1.rs"
+                        ));
+                    }
+                }
+
+                pub(crate) mod pipeline_ingress {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.pipeline_ingress.v1beta1.rs"
+                        ));
+                    }
+                }
+
+                pub(crate) mod plugin_bootstrap {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.plugin_bootstrap.v1beta1.rs"
+                        ));
+                    }
+                }
+
+                pub(crate) mod plugin_registry {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.plugin_registry.v1beta1.rs"
+                        ));
+                    }
+                }
+
+                pub(crate) mod plugin_sdk {
+                    pub(crate) mod generators {
+                        pub(crate) mod v1beta1 {
+                            include!(concat!(
+                                env!("OUT_DIR"),
+                                "/graplinc.grapl.api.plugin_sdk.generators.v1beta1.rs"
+                            ));
+                        }
+                    }
+                }
+
+                pub(crate) mod plugin_work_queue {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.plugin_work_queue.v1beta1.rs"
+                        ));
+                    }
+                }
+
+                #[cfg(feature = "uid-allocator")]
+                pub(crate) mod uid_allocator {
+                    pub(crate) mod v1beta1 {
+                        include!(concat!(
+                            env!("OUT_DIR"),
+                            "/graplinc.grapl.api.uid_allocator.v1beta1.rs"
+                        ));
+                    }
                 }
             }
-            pub(crate) mod organization_management {
+
+            pub(crate) mod pipeline {
                 pub(crate) mod v1beta1 {
                     include!(concat!(
                         env!("OUT_DIR"),
-                        "/graplinc.grapl.api.organization_management.v1beta1.rs"
+                        "/graplinc.grapl.pipeline.v1beta1.rs"
                     ));
                 }
-            }
-            pub(crate) mod plugin_bootstrap {
-                pub(crate) mod v1beta1 {
+
+                pub(crate) mod v1beta2 {
                     include!(concat!(
                         env!("OUT_DIR"),
-                        "/graplinc.grapl.api.plugin_bootstrap.v1beta1.rs"
+                        "/graplinc.grapl.pipeline.v1beta2.rs"
                     ));
                 }
             }
         }
-        pub(crate) mod pipeline {
-            pub mod v1beta1 {
-                include!(concat!(
-                    env!("OUT_DIR"),
-                    "/graplinc.grapl.pipeline.v1beta1.rs"
-                ));
+    }
+}
+
+// This module hierarchy closely emulates the one above, but contains our rust
+// language representations of the wire format. This module hierarchy must be
+// kept synchronized with the one above. This module hierarchy defines this
+// crate's public API.
+pub mod graplinc {
+    pub mod common {
+        pub mod v1beta1;
+    }
+
+    pub mod grapl {
+        pub mod api {
+            pub mod event_source {
+                pub mod v1beta1;
+            }
+
+            pub mod graph {
+                pub mod v1beta1;
+            }
+
+            pub mod model_plugin_deployer {
+                pub mod v1;
+            }
+
+            pub mod organization_management {
+                pub mod v1beta1;
             }
+
+            pub mod pipeline_ingress {
+                pub mod v1beta1;
+            }
+
+            pub mod plugin_bootstrap {
+                pub mod v1beta1;
+            }
+
+            pub mod plugin_registry {
+                pub mod v1beta1;
+                mod v1beta1_client;
+                mod v1beta1_server;
+            }
+
+            pub mod plugin_sdk {
+                pub mod generators {
+                    pub mod v1beta1;
+                }
+            }
+
+            pub mod plugin_work_queue {
+                pub mod v1beta1;
+                mod v1beta1_client;
+                mod v1beta1_server;
+            }
+
+            #[cfg(feature = "uid-allocator")]
+            pub mod uid_allocator {
+                pub mod v1beta1;
+            }
+        }
+
+        pub mod metrics {
+            pub mod v1;
+        }
+
+        pub mod pipeline {
+            pub mod v1beta1;
+            pub mod v1beta2;
         }
     }
 }
 
-pub mod graph_descriptions;
-pub use graph_descriptions::node_property;
+// This one's a little funky. See https://stackoverflow.com/a/53207767. The idea
+// here is that we _do not want_ to expose the TYPE_URL as part of this crate's
+// public API. However, in order to serialize a google.protobuf.Any message we
+// require the TYPE_URL. Since prost does not support protocol buffer reflection
+// features--descriptors, crucially--we are unable to determine the TYPE_URL for
+// an arbitrary protocol buffer type at runtime. Therefore we resort to
+// hard-coding them.
+//
+// The critical invariant here is that the TYPE_URL must be identically the same
+// for any protocol buffer as that generated by the python implementation of
+// google.protobuf.any_pb2.Any#Pack as used in python-proto. We maintain this
+// invariant with round-trip regression tests which ensure every message can
+// pass unmolested through all of our serialization and deserialization code.
+//
+// Should we find the need to work with protocol buffer stubs in a third
+// language we'll add those stubs to the round-trip tests as well.
+pub(crate) mod type_url {
+    pub trait TypeUrl {
+        const TYPE_URL: &'static str;
+    }
+}
+
+#[non_exhaustive]
+#[derive(Error, Debug)]
+pub enum SerDeError {
+    #[error("failed to serialize {0}")]
+    EncodingFailed(#[from] EncodeError),
+
+    #[error("failed to deserialize {0}")]
+    DecodingFailed(#[from] DecodeError),
 
-pub mod organization_management;
-pub mod pipeline;
-pub mod plugin_bootstrap;
-pub mod types;
+    #[error("bad timestamp {0}")]
+    BadTimestamp(#[from] SystemTimeError),
+
+    #[error("missing message field {0}")]
+    MissingField(&'static str),
+
+    #[error("field {field_name} failed assertion {assertion}")]
+    InvalidField {
+        field_name: &'static str,
+        assertion: String,
+    },
+
+    #[error("Unknown enum variant {0}")]
+    UnknownVariant(&'static str),
+}
+
+impl From<std::convert::Infallible> for SerDeError {
+    fn from(_: std::convert::Infallible) -> Self {
+        unimplemented!()
+    }
+}
+
+pub trait SerDe: type_url::TypeUrl + Clone + std::fmt::Debug {
+    fn serialize(self) -> Result<Bytes, SerDeError>;
+
+    fn deserialize<B>(buf: B) -> Result<Self, SerDeError>
+    where
+        B: Buf,
+        Self: Sized;
+}
+
+pub(crate) mod serde_impl {
+    use bytes::{
+        Bytes,
+        BytesMut,
+    };
+    use prost::Message;
+
+    use crate::{
+        type_url,
+        SerDe,
+        SerDeError,
+    };
+
+    /// You can use this blanket implementation to automatically implement SerDe
+    /// for a type YourType defined within this crate. YourType must implement
+    /// either From<proto::YourType> or TryFrom<proto::YourType> and you must
+    /// implement either From<YourType> or TryFrom<YourType> for
+    /// proto::YourType. YourType must also implement type_url::TypeUrl. If
+    /// YourType meets these conditions you can automatically implement SerDe by
+    /// implementing the simpler trait ProtobufSerializable.
+    ///
+    /// Example usage:
+    ///
+    /// use crate::{
+    ///     protobufs::graplinc::v1beta1 as proto,
+    ///     serde_impl,
+    ///     type_url,
+    /// };
+    ///
+    /// pub struct YourType {...}
+    ///
+    /// impl type_url::TypeUrl for YourType {
+    ///     const TYPE_URL: &'static str =
+    ///         "graplsecurity.com/graplinc.v1beta1.YourType";
+    /// }
+    ///
+    /// impl serde_impl::ProtobufSerializable for YourType {
+    ///    type ProtobufMessage = proto::YourType;
+    /// }
+    pub(crate) trait ProtobufSerializable: Sized {
+        type ProtobufMessage: TryFrom<Self> + TryInto<Self> + Message + Default;
+    }
+
+    impl<T> SerDe for T
+    where
+        T: ProtobufSerializable,
+        T: type_url::TypeUrl + Clone + std::fmt::Debug,
+        SerDeError: From<<<T as ProtobufSerializable>::ProtobufMessage as TryFrom<T>>::Error>,
+        SerDeError: From<<<T as ProtobufSerializable>::ProtobufMessage as TryInto<T>>::Error>,
+    {
+        fn serialize(self: T) -> Result<Bytes, SerDeError> {
+            let proto = T::ProtobufMessage::try_from(self)?;
+            let mut buf = BytesMut::with_capacity(proto.encoded_len());
+            proto.encode(&mut buf)?;
+            Ok(buf.freeze())
+        }
+
+        fn deserialize<B>(buf: B) -> Result<T, SerDeError>
+        where
+            B: bytes::Buf,
+            Self: Sized,
+        {
+            let proto: T::ProtobufMessage = Message::decode(buf)?;
+            Ok(proto.try_into()?)
+        }
+    }
+}
diff --git a/src/rust/rust-proto/src/organization_management.rs b/src/rust/rust-proto/src/organization_management.rs
deleted file mode 100644
index a4005c7931..0000000000
--- a/src/rust/rust-proto/src/organization_management.rs
+++ /dev/null
@@ -1,189 +0,0 @@
-pub use crate::graplinc::grapl::api::organization_management::v1beta1::{
-    organization_management_service_client,
-    organization_management_service_server,
-    CreateOrganizationRequest as CreateOrganizationRequestProto,
-    CreateOrganizationResponse as CreateOrganizationResponseProto,
-    CreateUserRequest as CreateUserRequestProto,
-    CreateUserResponse as CreateUserResponseProto,
-};
-
-#[derive(Debug, thiserror::Error)]
-pub enum OrganizationManagementDeserializationError {
-    #[error("Missing a required field: {0}")]
-    MissingRequiredField(&'static str),
-    #[error("Empty field: {0}")]
-    EmptyField(&'static str),
-}
-
-fn validate_string(
-    s: &str,
-    field_name: &'static str,
-) -> Result<(), OrganizationManagementDeserializationError> {
-    if s.is_empty() {
-        Err(OrganizationManagementDeserializationError::EmptyField(
-            field_name,
-        ))
-    } else {
-        Ok(())
-    }
-}
-
-fn validate_bytes(
-    s: &[u8],
-    field_name: &'static str,
-) -> Result<(), OrganizationManagementDeserializationError> {
-    if s.is_empty() {
-        Err(OrganizationManagementDeserializationError::EmptyField(
-            field_name,
-        ))
-    } else {
-        Ok(())
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct CreateOrganizationRequest {
-    pub organization_display_name: String,
-    pub admin_username: String,
-    pub admin_email: String,
-    pub admin_password: Vec<u8>,
-    pub should_reset_password: bool,
-}
-
-impl TryFrom<CreateOrganizationRequestProto> for CreateOrganizationRequest {
-    type Error = OrganizationManagementDeserializationError;
-
-    fn try_from(value: CreateOrganizationRequestProto) -> Result<Self, Self::Error> {
-        validate_string(
-            &value.organization_display_name,
-            "CreateOrganizationRequestProto.organization_display_name",
-        )?;
-        validate_string(
-            &value.admin_username,
-            "CreateOrganizationRequestProto.username",
-        )?;
-        validate_string(&value.admin_email, "CreateOrganizationRequestProto.email")?;
-        validate_bytes(
-            &value.admin_password,
-            "CreateOrganizationRequestProto.password",
-        )?;
-
-        Ok(Self {
-            organization_display_name: value.organization_display_name,
-            admin_username: value.admin_username,
-            admin_email: value.admin_email,
-            admin_password: value.admin_password,
-            should_reset_password: value.should_reset_password,
-        })
-    }
-}
-
-impl From<CreateOrganizationRequest> for CreateOrganizationRequestProto {
-    fn from(value: CreateOrganizationRequest) -> Self {
-        Self {
-            organization_display_name: value.organization_display_name,
-            admin_username: value.admin_username,
-            admin_email: value.admin_email,
-            admin_password: value.admin_password,
-            should_reset_password: value.should_reset_password,
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct CreateOrganizationResponse {
-    pub organization_id: uuid::Uuid,
-}
-
-impl TryFrom<CreateOrganizationResponseProto> for CreateOrganizationResponse {
-    type Error = OrganizationManagementDeserializationError;
-
-    fn try_from(value: CreateOrganizationResponseProto) -> Result<Self, Self::Error> {
-        let organization_id = value
-            .organization_id
-            .ok_or(Self::Error::MissingRequiredField(
-                "CreateUserRequest.organization_id",
-            ))?
-            .into();
-
-        Ok(Self { organization_id })
-    }
-}
-
-impl From<CreateOrganizationResponse> for CreateOrganizationResponseProto {
-    fn from(value: CreateOrganizationResponse) -> Self {
-        Self {
-            organization_id: Some(value.organization_id.into()),
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct CreateUserRequest {
-    pub organization_id: uuid::Uuid,
-    pub name: String,
-    pub email: String,
-    pub password: Vec<u8>,
-}
-
-impl TryFrom<CreateUserRequestProto> for CreateUserRequest {
-    type Error = OrganizationManagementDeserializationError;
-
-    fn try_from(value: CreateUserRequestProto) -> Result<Self, Self::Error> {
-        let organization_id = value
-            .organization_id
-            .ok_or(Self::Error::MissingRequiredField(
-                "CreateUserRequest.organization_id",
-            ))?
-            .into();
-        validate_string(&value.name, "CreateUserRequest.name")?;
-        validate_string(&value.email, "CreateUserRequest.email")?;
-        validate_bytes(&value.password, "CreateUserRequest.password")?;
-
-        Ok(Self {
-            organization_id,
-            name: value.name,
-            email: value.email,
-            password: value.password,
-        })
-    }
-}
-
-impl From<CreateUserRequest> for CreateUserRequestProto {
-    fn from(value: CreateUserRequest) -> Self {
-        Self {
-            organization_id: Some(value.organization_id.into()),
-            name: value.name,
-            email: value.email,
-            password: value.password,
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct CreateUserResponse {
-    pub user_id: uuid::Uuid,
-}
-
-impl TryFrom<CreateUserResponseProto> for CreateUserResponse {
-    type Error = OrganizationManagementDeserializationError;
-
-    fn try_from(value: CreateUserResponseProto) -> Result<Self, Self::Error> {
-        let user_id = value
-            .user_id
-            .ok_or(Self::Error::MissingRequiredField(
-                "CreateUserResponse.user_id",
-            ))?
-            .into();
-
-        Ok(Self { user_id })
-    }
-}
-
-impl From<CreateUserResponse> for CreateUserResponseProto {
-    fn from(value: CreateUserResponse) -> Self {
-        Self {
-            user_id: Some(value.user_id.into()),
-        }
-    }
-}
diff --git a/src/rust/rust-proto/src/pipeline.rs b/src/rust/rust-proto/src/pipeline.rs
deleted file mode 100644
index 0e8c0677a7..0000000000
--- a/src/rust/rust-proto/src/pipeline.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-pub use grapl_pipeline::{
-    Envelope,
-    Metadata,
-    RawLog,
-};
-
-pub use crate::graplinc::grapl::pipeline::v1beta1 as grapl_pipeline;
-
-pub trait ServiceMessage {
-    const TYPE_NAME: &'static str;
-}
diff --git a/src/rust/rust-proto/src/plugin_bootstrap.rs b/src/rust/rust-proto/src/plugin_bootstrap.rs
deleted file mode 100644
index d8145d44c0..0000000000
--- a/src/rust/rust-proto/src/plugin_bootstrap.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-use std::fmt::Formatter;
-
-pub use crate::graplinc::grapl::api::plugin_bootstrap::v1beta1::{
-    plugin_bootstrap_service_client::PluginBootstrapServiceClient,
-    plugin_bootstrap_service_server::{
-        PluginBootstrapService,
-        PluginBootstrapServiceServer,
-    },
-    ClientCertificate as ClientCertificateProto,
-    GetBootstrapRequest as GetBootstrapRequestProto,
-    GetBootstrapResponse as GetBootstrapResponseProto,
-    PluginPayload as PluginPayloadProto,
-};
-
-#[derive(Debug, thiserror::Error)]
-pub enum PluginBootstrapDeserializationError {
-    #[error("Missing a required field {0}")]
-    MissingRequiredField(&'static str),
-    #[error("Empty field {0}")]
-    EmptyField(&'static str),
-    #[error("Unknown variant {0}")]
-    UnknownVariant(std::borrow::Cow<'static, str>),
-}
-
-#[derive(Clone, Debug)]
-pub struct GetBootstrapRequest {}
-
-impl TryFrom<GetBootstrapRequestProto> for GetBootstrapRequest {
-    type Error = PluginBootstrapDeserializationError;
-
-    fn try_from(_: GetBootstrapRequestProto) -> Result<Self, Self::Error> {
-        Ok(GetBootstrapRequest {})
-    }
-}
-
-impl From<GetBootstrapRequest> for GetBootstrapRequestProto {
-    fn from(_: GetBootstrapRequest) -> Self {
-        GetBootstrapRequestProto {}
-    }
-}
-
-#[derive(Clone)]
-pub struct PluginPayload {
-    pub plugin_binary: Vec<u8>,
-}
-
-impl std::fmt::Debug for PluginPayload {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("PluginPayload")
-            .field("plugin_binary.len", &self.plugin_binary.len())
-            .finish()
-    }
-}
-
-impl TryFrom<PluginPayloadProto> for PluginPayload {
-    type Error = PluginBootstrapDeserializationError;
-
-    fn try_from(value: PluginPayloadProto) -> Result<Self, Self::Error> {
-        if value.plugin_binary.is_empty() {
-            return Err(PluginBootstrapDeserializationError::EmptyField(
-                "PluginPayload.plugin_binary",
-            ));
-        }
-        Ok(PluginPayload {
-            plugin_binary: value.plugin_binary,
-        })
-    }
-}
-
-impl From<PluginPayload> for PluginPayloadProto {
-    fn from(value: PluginPayload) -> Self {
-        PluginPayloadProto {
-            plugin_binary: value.plugin_binary,
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct ClientCertificate {
-    pub client_certificate: Vec<u8>,
-}
-
-impl std::fmt::Debug for ClientCertificate {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("ClientCertificate")
-            .field("client_certificate.len", &self.client_certificate)
-            .finish()
-    }
-}
-
-impl TryFrom<ClientCertificateProto> for ClientCertificate {
-    type Error = PluginBootstrapDeserializationError;
-
-    fn try_from(value: ClientCertificateProto) -> Result<Self, Self::Error> {
-        if value.client_certificate.is_empty() {
-            return Err(PluginBootstrapDeserializationError::EmptyField(
-                "ClientCertificateProto.client_certificate",
-            ));
-        }
-
-        Ok(Self {
-            client_certificate: value.client_certificate,
-        })
-    }
-}
-
-impl From<ClientCertificate> for ClientCertificateProto {
-    fn from(value: ClientCertificate) -> Self {
-        Self {
-            client_certificate: value.client_certificate,
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct GetBootstrapResponse {
-    pub plugin_payload: PluginPayload,
-    pub client_certificate: ClientCertificate,
-}
-
-impl TryFrom<GetBootstrapResponseProto> for GetBootstrapResponse {
-    type Error = PluginBootstrapDeserializationError;
-
-    fn try_from(value: GetBootstrapResponseProto) -> Result<Self, Self::Error> {
-        let plugin_payload = value
-            .plugin_payload
-            .ok_or(PluginBootstrapDeserializationError::MissingRequiredField(
-                "GetBootstrapResponseProto.plugin_payload",
-            ))?
-            .try_into()?;
-        let client_certificate = value
-            .client_certificate
-            .ok_or(PluginBootstrapDeserializationError::MissingRequiredField(
-                "GetBootstrapResponseProto.client_certificate",
-            ))?
-            .try_into()?;
-        Ok(GetBootstrapResponse {
-            plugin_payload,
-            client_certificate,
-        })
-    }
-}
-
-impl From<GetBootstrapResponse> for GetBootstrapResponseProto {
-    fn from(value: GetBootstrapResponse) -> Self {
-        Self {
-            plugin_payload: Some(value.plugin_payload.into()),
-            client_certificate: Some(value.client_certificate.into()),
-        }
-    }
-}
diff --git a/src/rust/rust-proto-new/src/protocol.rs b/src/rust/rust-proto/src/protocol.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/protocol.rs
rename to src/rust/rust-proto/src/protocol.rs
diff --git a/src/rust/rust-proto-new/src/protocol/healthcheck.rs b/src/rust/rust-proto/src/protocol/healthcheck.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/protocol/healthcheck.rs
rename to src/rust/rust-proto/src/protocol/healthcheck.rs
diff --git a/src/rust/rust-proto-new/src/protocol/service_client.rs b/src/rust/rust-proto/src/protocol/service_client.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/protocol/service_client.rs
rename to src/rust/rust-proto/src/protocol/service_client.rs
diff --git a/src/rust/rust-proto-new/src/protocol/status.rs b/src/rust/rust-proto/src/protocol/status.rs
similarity index 98%
rename from src/rust/rust-proto-new/src/protocol/status.rs
rename to src/rust/rust-proto/src/protocol/status.rs
index 4d9bf0b5b8..cbedc1828a 100644
--- a/src/rust/rust-proto-new/src/protocol/status.rs
+++ b/src/rust/rust-proto/src/protocol/status.rs
@@ -47,9 +47,9 @@ pub enum Code {
 impl Code {
     /// Get description of this `Code`.
     /// ```
-    /// fn make_rpc_request() -> rust_proto_new::protocol::status::Code {
+    /// fn make_rpc_request() -> rust_proto::protocol::status::Code {
     ///     // ...
-    ///     rust_proto_new::protocol::status::Code::Ok
+    ///     rust_proto::protocol::status::Code::Ok
     /// }
     /// let code = make_rpc_request();
     /// println!("Operation completed. Human readable description: {}", code.description());
diff --git a/src/rust/rust-proto-new/src/protocol/tls.rs b/src/rust/rust-proto/src/protocol/tls.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/protocol/tls.rs
rename to src/rust/rust-proto/src/protocol/tls.rs
diff --git a/src/rust/rust-proto-new/src/server_internals.rs b/src/rust/rust-proto/src/server_internals.rs
similarity index 100%
rename from src/rust/rust-proto-new/src/server_internals.rs
rename to src/rust/rust-proto/src/server_internals.rs
diff --git a/src/rust/rust-proto/src/types.rs b/src/rust/rust-proto/src/types.rs
deleted file mode 100644
index 381fe7ac17..0000000000
--- a/src/rust/rust-proto/src/types.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use crate::graplinc::common::v1beta1 as graplinc_common;
-
-impl graplinc_common::Uuid {
-    pub fn new() -> Self {
-        Self::from(uuid::Uuid::new_v4())
-    }
-}
-
-impl From<graplinc_common::Uuid> for uuid::Uuid {
-    fn from(p: graplinc_common::Uuid) -> Self {
-        let lsb: [u8; 8] = p.lsb.to_le_bytes();
-        let msb: [u8; 8] = p.msb.to_le_bytes();
-        let u: u128 = u128::from_le_bytes([
-            lsb[0], lsb[1], lsb[2], lsb[3], lsb[4], lsb[5], lsb[6], lsb[7], msb[0], msb[1], msb[2],
-            msb[3], msb[4], msb[5], msb[6], msb[7],
-        ]);
-
-        uuid::Builder::from_u128(u).build()
-    }
-}
-
-impl From<uuid::Uuid> for graplinc_common::Uuid {
-    fn from(u: uuid::Uuid) -> Self {
-        let u = u.as_u128();
-        let u_le: [u8; 16] = u.to_le_bytes();
-        // Ugly, but `Index` isn't const, so Rust can't figure
-        // out that half of a [u8; 16] is two [u8; 8]'s
-        let lsb: u64 = u64::from_le_bytes([
-            u_le[0], u_le[1], u_le[2], u_le[3], u_le[4], u_le[5], u_le[6], u_le[7],
-        ]);
-        let msb: u64 = u64::from_le_bytes([
-            u_le[8], u_le[9], u_le[10], u_le[11], u_le[12], u_le[13], u_le[14], u_le[15],
-        ]);
-        Self { lsb, msb }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_equality_from_uuid() {
-        for _ in 0..1000 {
-            // start with a uuid::Uuid
-            let u0 = uuid::Uuid::new_v4();
-            // Convert it into a protobuf Uuid
-            let pu: graplinc_common::Uuid = u0.clone().into();
-            // Then back to a uuid::Uuid
-            let u1: uuid::Uuid = pu.into();
-            // Hopefully it hasn't changed
-            assert_eq!(u0, u1);
-        }
-    }
-
-    #[test]
-    fn test_equality_from_proto_uuid() {
-        for _ in 0..1000 {
-            // start with a protobuf Uuid
-            let pu0 = graplinc_common::Uuid::new();
-            // Convert it into a uuid::Uuid
-            let u: uuid::Uuid = pu0.clone().into();
-            // Then back to a protobuf Uuid
-            let pu1: graplinc_common::Uuid = u.into();
-            // Hopefully it hasn't changed
-            assert_eq!(pu0, pu1);
-        }
-    }
-}
diff --git a/src/rust/sqs-executor/Cargo.toml b/src/rust/sqs-executor/Cargo.toml
deleted file mode 100644
index 1efcfdf8cc..0000000000
--- a/src/rust/sqs-executor/Cargo.toml
+++ /dev/null
@@ -1,48 +0,0 @@
-[package]
-name = "sqs-executor"
-version = "0.1.0"
-authors = ["colin <colin@graplsecurity.com>"]
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-[features]
-integration = []
-
-[dependencies]
-grapl-observe = { path = "../grapl-observe" }
-grapl-utils = { path = "../grapl-utils" }
-rust-proto = { path = "../rust-proto/" }
-rusoto_core = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-rusoto_s3 = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-rusoto_sqs = { version = "0.47.0", default_features = false, features = [
-  "rustls"
-] }
-tokio = { version = "1.17", features = [
-  "io-util",
-  "macros",
-  "rt",
-  "rt-multi-thread",
-  "sync",
-  "time",
-] }
-redis = { version = "0.21.5", features = ["tokio-comp", "connection-manager"] }
-serde = "1.0.130"
-serde_json = "1.0.72"
-prost = "0.9.0"
-thiserror = "1.0.30"
-async-trait = "0.1.51"
-tracing = "0.1.29"
-tracing-futures = "0.2.5"
-futures-util = "0.3"
-uuid = { version = "0.8.2", features = ["v4"] }
-lazy_static = "1.4.0"
-futures = "0.3"
-num_cpus = "1.13.0"
-hex = "0.4.3"
-chrono = "0.4.19"
-lru = "0.7.0"
-itertools = "0.10.1"
diff --git a/src/rust/sqs-executor/src/cache.rs b/src/rust/sqs-executor/src/cache.rs
deleted file mode 100644
index ce5414a073..0000000000
--- a/src/rust/sqs-executor/src/cache.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-use std::{
-    collections::hash_map::DefaultHasher,
-    hash::{
-        Hash,
-        Hasher,
-    },
-};
-
-use async_trait::async_trait;
-
-use crate::errors::{
-    CheckedError,
-    Recoverable,
-};
-
-pub trait Cacheable {
-    fn identity(&self) -> Vec<u8>;
-}
-
-impl<H> Cacheable for H
-where
-    H: Hash,
-{
-    fn identity(&self) -> Vec<u8> {
-        let mut hasher = DefaultHasher::new();
-        self.hash(&mut hasher);
-        let hash = hasher.finish();
-        hash.to_le_bytes().to_vec()
-    }
-}
-
-#[async_trait]
-pub trait Cache: Clone + Send + Sync {
-    type CacheErrorT: CheckedError + Send + Sync + 'static;
-
-    async fn all_exist<CA>(&mut self, cacheables: &[CA]) -> bool
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        // If cacheable doesn't return from filter_cached then
-        // we know it exists in the cache
-        self.filter_cached(cacheables).await.is_empty()
-    }
-
-    async fn store<CA>(&mut self, cacheable: CA) -> Result<(), Self::CacheErrorT>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static;
-
-    async fn store_all<CA>(&mut self, cacheables: &[CA]) -> Result<(), Self::CacheErrorT>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        for cacheable in cacheables.into_iter() {
-            self.store(cacheable.identity()).await?;
-        }
-        Ok(())
-    }
-
-    async fn filter_cached<CA>(&mut self, cacheables: &[CA]) -> Vec<CA>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static;
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum NopCacheError {
-    #[error("NopCache never errors")]
-    Never,
-}
-
-impl CheckedError for NopCacheError {
-    fn error_type(&self) -> Recoverable {
-        panic!("NopCache can not error")
-    }
-}
-
-#[derive(Clone, Copy)]
-pub struct NopCache {}
-
-#[async_trait]
-impl Cache for NopCache {
-    type CacheErrorT = NopCacheError;
-
-    async fn all_exist<CA>(&mut self, _cacheables: &[CA]) -> bool
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        false
-    }
-
-    async fn store<CA>(&mut self, _identity: CA) -> Result<(), Self::CacheErrorT>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        tracing::debug!("nopcache.store operation");
-        Ok(())
-    }
-
-    async fn filter_cached<CA>(&mut self, cacheables: &[CA]) -> Vec<CA>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        cacheables.to_vec()
-    }
-}
diff --git a/src/rust/sqs-executor/src/completion_event_serializer.rs b/src/rust/sqs-executor/src/completion_event_serializer.rs
deleted file mode 100644
index cf151db635..0000000000
--- a/src/rust/sqs-executor/src/completion_event_serializer.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-pub trait CompletionEventSerializer {
-    type CompletedEvent;
-    type Output;
-    type Error;
-    fn serialize_completed_events(
-        &mut self,
-        completed_events: &[Self::CompletedEvent],
-    ) -> Result<Vec<Self::Output>, Self::Error>;
-}
diff --git a/src/rust/sqs-executor/src/errors.rs b/src/rust/sqs-executor/src/errors.rs
deleted file mode 100644
index 87928b5f37..0000000000
--- a/src/rust/sqs-executor/src/errors.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use thiserror::Error;
-
-#[derive(Eq, PartialEq, Debug)]
-pub enum Recoverable {
-    Transient,
-    Persistent,
-}
-
-pub trait CheckedError: std::error::Error {
-    fn error_type(&self) -> Recoverable;
-
-    fn is_transient(&self) -> bool {
-        self.error_type() == Recoverable::Transient
-    }
-
-    fn is_persistent(&self) -> bool {
-        self.error_type() == Recoverable::Persistent
-    }
-}
-
-#[derive(Error, Debug)]
-pub enum ExecutorError<CacheErrorT, HandlerErrorT>
-where
-    CacheErrorT: CheckedError,
-    HandlerErrorT: CheckedError,
-{
-    #[error("Failed to receive sqs messages")]
-    SqsReceiveError(#[from] rusoto_core::RusotoError<rusoto_sqs::ReceiveMessageError>),
-    #[error("Cache error")]
-    CacheError(CacheErrorT),
-    #[error("Handler error")]
-    HandlerError(HandlerErrorT),
-}
-
-impl<CacheErrorT, HandlerErrorT> CheckedError for ExecutorError<CacheErrorT, HandlerErrorT>
-where
-    CacheErrorT: CheckedError,
-    HandlerErrorT: CheckedError,
-{
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::SqsReceiveError(_e) => Recoverable::Transient,
-            Self::CacheError(e) => e.error_type(),
-            Self::HandlerError(e) => e.error_type(),
-        }
-    }
-}
diff --git a/src/rust/sqs-executor/src/event_decoder.rs b/src/rust/sqs-executor/src/event_decoder.rs
deleted file mode 100644
index dae47f8941..0000000000
--- a/src/rust/sqs-executor/src/event_decoder.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use crate::errors::CheckedError;
-
-pub trait PayloadDecoder<E> {
-    type DecoderError: CheckedError;
-    fn decode(&mut self, bytes: Vec<u8>) -> Result<E, Self::DecoderError>;
-}
-
-impl<T, F, E> PayloadDecoder<T> for F
-where
-    F: Fn(Vec<u8>) -> Result<T, E>,
-    E: CheckedError,
-{
-    type DecoderError = E;
-    fn decode(&mut self, body: Vec<u8>) -> Result<T, Self::DecoderError> {
-        (self)(body)
-    }
-}
diff --git a/src/rust/sqs-executor/src/event_emitter.rs b/src/rust/sqs-executor/src/event_emitter.rs
deleted file mode 100644
index 38b5cffbbc..0000000000
--- a/src/rust/sqs-executor/src/event_emitter.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use async_trait::async_trait;
-
-#[async_trait]
-pub trait EventEmitter {
-    type Event;
-    type Error: std::fmt::Debug + Send;
-    async fn emit_event(&mut self, completed_events: Vec<Self::Event>) -> Result<(), Self::Error>;
-}
diff --git a/src/rust/sqs-executor/src/event_handler.rs b/src/rust/sqs-executor/src/event_handler.rs
deleted file mode 100644
index abdb19e533..0000000000
--- a/src/rust/sqs-executor/src/event_handler.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use std::fmt::Debug;
-
-use async_trait::async_trait;
-
-use crate::{
-    cache::Cacheable,
-    errors::CheckedError,
-    event_status::EventStatus,
-};
-
-#[derive(Default)]
-pub struct CompletedEvents {
-    pub identities: Vec<(Vec<u8>, EventStatus)>,
-}
-
-impl CompletedEvents {
-    pub fn clear(&mut self) {
-        self.identities.clear();
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.identities.is_empty()
-    }
-
-    pub fn len(&self) -> usize {
-        self.identities.len()
-    }
-
-    pub fn add_identity(&mut self, identity: impl Cacheable, r: impl Into<EventStatus>) {
-        self.identities.push((identity.identity(), r.into()));
-    }
-}
-
-#[async_trait]
-pub trait EventHandler {
-    type InputEvent;
-    type OutputEvent: Clone + Send + Sync + 'static;
-    type Error: Debug + CheckedError + Send + Sync + 'static;
-
-    async fn handle_event(
-        &mut self,
-        input: Self::InputEvent,
-        identities: &mut CompletedEvents,
-    ) -> Result<Self::OutputEvent, Result<(Self::OutputEvent, Self::Error), Self::Error>>;
-}
diff --git a/src/rust/sqs-executor/src/event_status.rs b/src/rust/sqs-executor/src/event_status.rs
deleted file mode 100644
index 081cd75a6f..0000000000
--- a/src/rust/sqs-executor/src/event_status.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-type PartialResult<T, E> = Result<T, Result<(T, E), E>>;
-
-#[derive(Clone, Debug)]
-pub enum EventStatus {
-    Success,
-    Partial,
-    Failure,
-}
-
-impl<T, E> From<&PartialResult<T, E>> for EventStatus {
-    fn from(r: &PartialResult<T, E>) -> Self {
-        match r {
-            Ok(_) => EventStatus::Success,
-            Err(Ok((_, _))) => EventStatus::Partial,
-            Err(Err(_)) => EventStatus::Failure,
-        }
-    }
-}
diff --git a/src/rust/sqs-executor/src/key_creator.rs b/src/rust/sqs-executor/src/key_creator.rs
deleted file mode 100644
index ee65c427ba..0000000000
--- a/src/rust/sqs-executor/src/key_creator.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-//<prefix>-<event-type>/<day>/<seconds>/<serialization>/<compression>-<capability>
-
-use std::time::{
-    SystemTime,
-    UNIX_EPOCH,
-};
-
-pub trait KeyGenerator {
-    fn generate_key(&mut self, capability: u64) -> String;
-}
-
-pub struct S3KeyGenerator {
-    bucket: String,
-    serialization: String,
-    compression: String,
-}
-
-impl S3KeyGenerator {
-    pub fn new(
-        bucket: impl Into<String>,
-        serialization: impl Into<String>,
-        compression: impl Into<String>,
-    ) -> Self {
-        Self {
-            bucket: bucket.into(),
-            serialization: serialization.into(),
-            compression: compression.into(),
-        }
-    }
-}
-
-impl KeyGenerator for S3KeyGenerator {
-    fn generate_key(&mut self, capability: u64) -> String {
-        let cur_secs = match SystemTime::now().duration_since(UNIX_EPOCH) {
-            Ok(n) => n.as_secs(),
-            Err(_) => panic!("SystemTime before UNIX EPOCH!"),
-        };
-
-        let cur_day = cur_secs - (cur_secs % 86400);
-
-        format!(
-            "{bucket}/{day}/{seconds}/{serialization}/{compression}-{capability}",
-            bucket = self.bucket,
-            day = cur_day,
-            seconds = cur_secs,
-            serialization = self.serialization,
-            compression = self.compression,
-            capability = capability,
-        )
-    }
-}
-
-pub struct ZstdProtoKeyGenerator(S3KeyGenerator);
-
-impl ZstdProtoKeyGenerator {
-    pub fn new(bucket: impl Into<String>) -> Self {
-        Self(S3KeyGenerator::new(bucket, "proto_v0", "zstd_nodict"))
-    }
-}
-
-impl KeyGenerator for ZstdProtoKeyGenerator {
-    fn generate_key(&mut self, capability: u64) -> String {
-        self.0.generate_key(capability)
-    }
-}
diff --git a/src/rust/sqs-executor/src/lib.rs b/src/rust/sqs-executor/src/lib.rs
deleted file mode 100644
index 0414f31e2a..0000000000
--- a/src/rust/sqs-executor/src/lib.rs
+++ /dev/null
@@ -1,705 +0,0 @@
-use std::{
-    error::Error,
-    fmt::Debug,
-    future::Future,
-    io::Stdout,
-    panic::AssertUnwindSafe,
-    time::{
-        Duration,
-        SystemTime,
-        UNIX_EPOCH,
-    },
-};
-
-use event_emitter::EventEmitter;
-use event_handler::EventHandler;
-use futures_util::FutureExt;
-use grapl_observe::{
-    metric_reporter::{
-        tag,
-        MetricReporter,
-    },
-    timers::TimedFutureExt,
-};
-use prost::Message;
-pub use retriever::{
-    event_retriever,
-    s3_event_retriever,
-};
-use rusoto_core::RusotoError;
-use rusoto_s3::S3;
-use rusoto_sqs::{
-    ListQueuesError,
-    ListQueuesRequest,
-    Message as SqsMessage,
-    Sqs,
-};
-use rust_proto::pipeline::ServiceMessage;
-use s3_event_emitter::S3EventEmitter;
-use s3_event_retriever::S3PayloadRetriever;
-use tracing::{
-    debug,
-    error,
-    info,
-};
-
-use crate::{
-    cache::Cache,
-    completion_event_serializer::CompletionEventSerializer,
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-    event_decoder::PayloadDecoder,
-    event_handler::CompletedEvents,
-    event_retriever::PayloadRetriever,
-    event_status::EventStatus,
-    sqs_timeout_manager::keep_alive,
-};
-
-pub mod retriever;
-
-pub mod cache;
-pub mod completion_event_serializer;
-pub mod errors;
-pub mod event_decoder;
-pub mod event_emitter;
-pub mod event_handler;
-pub mod event_status;
-pub mod key_creator;
-pub mod redis_cache;
-pub mod rusoto_helpers;
-pub mod s3_event_emitter;
-pub mod sqs_timeout_manager;
-
-pub async fn make_ten<F, T>(f: F) -> [T; 10]
-where
-    F: Future<Output = T>,
-    T: Clone,
-{
-    let t = f.await;
-
-    [
-        t.clone(),
-        t.clone(),
-        t.clone(),
-        t.clone(),
-        t.clone(),
-        t.clone(),
-        t.clone(),
-        t.clone(),
-        t.clone(),
-        t,
-    ]
-}
-
-async fn cache_completed<CacheT>(cache: &mut CacheT, completed: &mut CompletedEvents)
-where
-    CacheT: Cache + Send,
-{
-    //todo: lift, or avoid this entirely
-    let mut to_cache = Vec::with_capacity(completed.len());
-
-    for (identity, status) in completed.identities.drain(..) {
-        match status {
-            EventStatus::Success | EventStatus::Failure => {
-                to_cache.push(identity);
-            }
-            _ => (),
-        }
-    }
-
-    cache.store_all(&to_cache).await.unwrap_or_else(|e| {
-        error!(
-            error = e.to_string().as_str(),
-            "Failed to store_all in cache"
-        )
-    });
-}
-
-#[tracing::instrument(skip(
-    next_message,
-    queue_url,
-    dead_letter_queue_url,
-    cache,
-    sqs_client,
-    event_handler,
-    s3_payload_retriever,
-    s3_emitter,
-    serializer,
-    metric_reporter,
-))]
-async fn process_message<
-    CacheT,
-    SInit,
-    SqsT,
-    DecoderT,
-    DecoderErrorT,
-    EventHandlerT,
-    InputEventT,
-    OutputEventT,
-    HandlerErrorT,
-    SerializerErrorT,
-    S3ClientT,
-    F,
-    CompletionEventSerializerT,
->(
-    next_message: SqsMessage,
-    queue_url: String,
-    dead_letter_queue_url: String,
-    cache: &mut CacheT,
-    sqs_client: SqsT,
-    event_handler: &mut EventHandlerT,
-    s3_payload_retriever: &mut S3PayloadRetriever<
-        S3ClientT,
-        SInit,
-        DecoderT,
-        InputEventT,
-        DecoderErrorT,
-    >,
-    s3_emitter: &mut S3EventEmitter<S3ClientT, F>,
-    serializer: &mut CompletionEventSerializerT,
-    mut metric_reporter: MetricReporter<Stdout>,
-) where
-    CacheT: crate::cache::Cache + Clone + Send + Sync + 'static,
-    SInit: (Fn(String) -> S3ClientT) + Clone + Send + Sync + 'static,
-    SqsT: Sqs + Clone + Send + Sync + 'static,
-    DecoderT: PayloadDecoder<InputEventT, DecoderError = DecoderErrorT> + Clone + Send + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-    InputEventT: Send,
-    EventHandlerT:
-        EventHandler<InputEvent = InputEventT, OutputEvent = OutputEventT, Error = HandlerErrorT>,
-    OutputEventT: ServiceMessage + Clone + Send + Sync + 'static,
-    HandlerErrorT: CheckedError + Debug + Send + Sync + 'static,
-    SerializerErrorT: Error + Debug + Send + Sync + 'static,
-    S3ClientT: S3 + Clone + Send + Sync + 'static,
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-    CompletionEventSerializerT: CompletionEventSerializer<
-        CompletedEvent = OutputEventT,
-        Output = Vec<u8>,
-        Error = SerializerErrorT,
-    >,
-{
-    let message_id = next_message.message_id.as_ref().unwrap().as_str();
-    let inner_loop_span = tracing::trace_span!(
-        "inner_loop_span",
-        message_id = message_id,
-        trace_id = tracing::field::Empty
-    );
-    let _enter = inner_loop_span.enter();
-
-    if cache.all_exist(&[message_id.to_owned()]).await {
-        debug!(
-            message_id = message_id,
-            "Message has already been processed",
-        );
-        rusoto_helpers::delete_message(
-            sqs_client.clone(),
-            queue_url.to_owned(),
-            next_message.receipt_handle.expect("missing receipt_handle"),
-            metric_reporter,
-        )
-        .await
-        .unwrap_or_else(|e| error!(message="delete_message failed", error=?e));
-        return;
-    }
-    debug!(message = "Retrieving payload from s3");
-    let receipt_handle = next_message
-        .receipt_handle
-        .as_ref()
-        .expect("missing receipt_handle")
-        .to_owned();
-    // Maintain an invisibility timeout for the message until we're done
-    let msg_handle = keep_alive(
-        sqs_client.clone(),
-        receipt_handle.clone(),
-        message_id.to_owned(),
-        queue_url.clone(),
-        30,
-    );
-    let payload = s3_payload_retriever.retrieve_event(&next_message).await;
-
-    let (meta, events) = match payload {
-        Ok(Some((meta, events))) => (meta, events),
-        Ok(None) => {
-            rusoto_helpers::delete_message(
-                sqs_client.clone(),
-                queue_url.to_owned(),
-                receipt_handle,
-                metric_reporter.clone(),
-            );
-            return;
-        }
-        Err(e) => {
-            error!(
-                queue_url = queue_url.as_str(),
-                message_id = message_id,
-                error = e.to_string().as_str(),
-                "Failed to retrieve payload with"
-            );
-            if let Recoverable::Persistent = e.error_type() {
-                rusoto_helpers::move_to_dead_letter(
-                    sqs_client.clone(),
-                    next_message.body.as_ref().unwrap(),
-                    dead_letter_queue_url,
-                    queue_url.to_owned(),
-                    receipt_handle,
-                    metric_reporter.clone(),
-                )
-                .await
-                .unwrap_or_else(|e| error!(message="move_to_dead_letter failed", error=?e));
-            }
-            return;
-        }
-    };
-
-    let trace_id: uuid::Uuid = meta.trace_id.clone().unwrap().into();
-    let trace_id = trace_id.to_string();
-    inner_loop_span.record("trace_id", &trace_id.as_str());
-
-    // todo: We can lift this
-    let mut completed = CompletedEvents::default();
-
-    let processing_result = async {
-        let (processing_result, ms) = event_handler
-            .handle_event(events, &mut completed)
-            .timed()
-            .await;
-        metric_reporter
-            .histogram(
-                "event_handler.handle_event",
-                ms as f64,
-                &[tag("success", processing_result.is_ok())],
-            )
-            .unwrap_or_else(
-                |e| error!(message="failed to report event_handler.handle_event.ms", error=?e),
-            );
-        processing_result
-    }
-    .await;
-
-    match processing_result {
-        Ok(total) => {
-            // encode event
-            let events = serializer
-                .serialize_completed_events(&[total])
-                .expect("Serializing failed");
-
-            if events.is_empty() {
-                tracing::debug!(message = "Serialized events produced no output");
-                return;
-            }
-
-            let mut envelopes = vec![];
-            for event in events {
-                let envelope = rust_proto::pipeline::Envelope {
-                    metadata: Some(meta.clone()),
-                    inner_type: OutputEventT::TYPE_NAME.to_string(),
-                    inner_message: event,
-                };
-                let mut encoded = vec![];
-                if let Err(e) = envelope.encode(&mut encoded) {
-                    tracing::error!(message="Failed to encode message", error=?e);
-                    continue;
-                };
-                envelopes.push(encoded);
-            }
-
-            // emit event
-            // todo: we should retry event emission
-            s3_emitter
-                .emit_event(envelopes)
-                .await
-                .expect("Failed to emit event");
-
-            cache
-                .store(next_message.message_id.clone().unwrap().into_bytes())
-                .await
-                .unwrap_or_else(|e| error!(message="cache.store failed", error=?e));
-            cache_completed(cache, &mut completed).await;
-            // ack the message - we could probably not block on this
-
-            msg_handle.stop();
-            rusoto_helpers::delete_message(
-                sqs_client.clone(),
-                queue_url.to_owned(),
-                receipt_handle,
-                metric_reporter.clone(),
-            )
-            .await
-            .unwrap_or_else(|e| error!(message="delete_message failed", error=?e));
-        }
-        Err(Ok((partial, e))) => {
-            error!(
-                message="EventHandler failed",
-                error=?e,
-                recoverable=?e.error_type()
-            );
-            let events = serializer
-                .serialize_completed_events(&[partial])
-                .expect("Serializing failed");
-            // emit event
-            // todo: we should retry event emission
-            if events.is_empty() {
-                tracing::debug!(message = "Serialized events produced no output");
-                return;
-            }
-
-            let mut envelopes = vec![];
-            for event in events {
-                let envelope = rust_proto::pipeline::Envelope {
-                    metadata: Some(meta.clone()),
-                    inner_type: OutputEventT::TYPE_NAME.to_string(),
-                    inner_message: event,
-                };
-                let mut encoded = vec![];
-                if let Err(e) = envelope.encode(&mut encoded) {
-                    tracing::error!(message="Failed to encode message", error=?e);
-                    continue;
-                };
-                envelopes.push(encoded);
-            }
-
-            // emit event
-            // todo: we should retry event emission
-            s3_emitter
-                .emit_event(envelopes)
-                .await
-                .expect("Failed to emit event");
-            cache_completed(cache, &mut completed).await;
-
-            if let Recoverable::Persistent = e.error_type() {
-                msg_handle.stop();
-                rusoto_helpers::move_to_dead_letter(
-                    sqs_client.clone(),
-                    next_message.body.as_ref().unwrap(),
-                    dead_letter_queue_url,
-                    queue_url.to_owned(),
-                    receipt_handle,
-                    metric_reporter.clone(),
-                )
-                .await
-                .unwrap_or_else(|e| error!(message="move_to_dead_letter failed", error=?e));
-            }
-        }
-        Err(Err(e)) => {
-            error!(
-                "Handler failed with: {:?} Recoverable: {:?}",
-                e,
-                e.error_type()
-            );
-            if let Recoverable::Persistent = e.error_type() {
-                msg_handle.stop();
-                rusoto_helpers::move_to_dead_letter(
-                    sqs_client.clone(),
-                    next_message.body.as_ref().unwrap(),
-                    dead_letter_queue_url,
-                    queue_url.to_owned(),
-                    receipt_handle,
-                    metric_reporter.clone(),
-                )
-                .await
-                .unwrap_or_else(|e| error!(message="move_to_dead_letter failed", error=?e));
-            }
-            // should we retry? idk
-            // otherwise we can just do nothing
-        }
-    }
-}
-
-async fn _process_loop<
-    CacheT,
-    SInit,
-    SqsT,
-    DecoderT,
-    DecoderErrorT,
-    EventHandlerT,
-    InputEventT,
-    OutputEventT,
-    HandlerErrorT,
-    SerializerErrorT,
-    S3ClientT,
-    F,
-    CompletionEventSerializerT,
->(
-    queue_url: String,
-    dead_letter_queue_url: String,
-    cache: &mut [CacheT; 10],
-    sqs_client: SqsT,
-    event_handler: &mut [EventHandlerT; 10],
-    s3_payload_retriever: &mut [S3PayloadRetriever<S3ClientT, SInit, DecoderT, InputEventT, DecoderErrorT>;
-             10],
-    s3_emitter: &mut [S3EventEmitter<S3ClientT, F>; 10],
-    serializer: &mut [CompletionEventSerializerT; 10],
-    mut metric_reporter: MetricReporter<Stdout>,
-) where
-    CacheT: crate::cache::Cache + Clone + Send + Sync + 'static,
-    SInit: (Fn(String) -> S3ClientT) + Clone + Send + Sync + 'static,
-    SqsT: Sqs + Clone + Send + Sync + 'static,
-    DecoderT: PayloadDecoder<InputEventT, DecoderError = DecoderErrorT> + Clone + Send + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-    InputEventT: Send,
-    EventHandlerT:
-        EventHandler<InputEvent = InputEventT, OutputEvent = OutputEventT, Error = HandlerErrorT>,
-    OutputEventT: ServiceMessage + Clone + Send + Sync + 'static,
-    HandlerErrorT: CheckedError + Debug + Send + Sync + 'static,
-    SerializerErrorT: Error + Debug + Send + Sync + 'static,
-    S3ClientT: S3 + Clone + Send + Sync + 'static,
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-    CompletionEventSerializerT: CompletionEventSerializer<
-        CompletedEvent = OutputEventT,
-        Output = Vec<u8>,
-        Error = SerializerErrorT,
-    >,
-{
-    let mut i = 1;
-    loop {
-        if i >= 15 {
-            i = 2;
-        }
-
-        let span = tracing::trace_span!("inner_process_loop", queue_url = queue_url.as_str(),);
-        let _enter = span.enter();
-        let message_batch = rusoto_helpers::get_message(
-            queue_url.to_string(),
-            sqs_client.clone(),
-            &mut metric_reporter,
-        )
-        .await;
-
-        let message_batch = match message_batch {
-            Ok(message_batch) => {
-                i = 1;
-                message_batch
-            }
-            Err(e) => {
-                error!(
-                    queue_url = queue_url.as_str(),
-                    error = e.to_string().as_str(),
-                    "Failed to get_message from queue"
-                );
-                tokio::time::sleep(std::time::Duration::from_millis(i * 250)).await;
-                i += 1;
-                continue;
-            }
-        };
-        let message_batch_len = message_batch.len();
-
-        info!(message_batch_len = message_batch_len, "Received messages");
-
-        if message_batch.is_empty() {
-            tokio::time::sleep(std::time::Duration::from_millis(250)).await;
-            continue;
-        }
-        let combos = message_batch
-            .into_iter()
-            .zip(&mut *event_handler)
-            .zip(&mut *s3_payload_retriever)
-            .zip(&mut *s3_emitter)
-            .zip(&mut *serializer)
-            .zip(&mut *cache);
-
-        let mut process_futs = Vec::with_capacity(10);
-
-        for combo in combos {
-            let (
-                ((((next_message, event_handler), s3_payload_retriever), s3_emitter), serializer),
-                cache,
-            ) = combo;
-            let p = process_message(
-                next_message,
-                queue_url.clone(),
-                dead_letter_queue_url.clone(),
-                cache,
-                sqs_client.clone(),
-                event_handler,
-                s3_payload_retriever,
-                s3_emitter,
-                serializer,
-                metric_reporter.clone(),
-            );
-            process_futs.push(p);
-        }
-
-        let all_processing = tokio::time::timeout(
-            Duration::from_secs(30 * 15),
-            futures::future::join_all(process_futs).timed(),
-        );
-        match all_processing.await {
-            Ok((_r, ms)) => {
-                metric_reporter
-                    .histogram("sqs_executor.all_processing.ms", ms as f64, &[])
-                    .unwrap_or_else(|e| {
-                        error!("failed to report sqs_executor.all_processing.ms: {:?}", e)
-                    });
-            }
-            Err(e) => error!("Timed out when processing messages: {:?}", e),
-        };
-    }
-}
-
-#[tracing::instrument(skip(
-    cache,
-    sqs_client,
-    event_handler,
-    s3_payload_retriever,
-    s3_emitter,
-    serializer,
-    metric_reporter,
-))]
-pub async fn process_loop<
-    CacheT,
-    SInit,
-    SqsT,
-    DecoderT,
-    DecoderErrorT,
-    EventHandlerT,
-    InputEventT,
-    OutputEventT,
-    HandlerErrorT,
-    SerializerErrorT,
-    S3ClientT,
-    F,
-    CompletionEventSerializerT,
->(
-    queue_url: String,
-    dead_letter_queue_url: String,
-    cache: &mut [CacheT; 10],
-    sqs_client: SqsT,
-    event_handler: &mut [EventHandlerT; 10],
-    s3_payload_retriever: &mut [S3PayloadRetriever<S3ClientT, SInit, DecoderT, InputEventT, DecoderErrorT>;
-             10],
-    s3_emitter: &mut [S3EventEmitter<S3ClientT, F>; 10],
-    serializer: &mut [CompletionEventSerializerT; 10],
-    metric_reporter: MetricReporter<Stdout>,
-) where
-    CacheT: crate::cache::Cache + Clone + Send + Sync + 'static,
-    SInit: (Fn(String) -> S3ClientT) + Clone + Send + Sync + 'static,
-    SqsT: Sqs + Clone + Send + Sync + 'static,
-    DecoderT: PayloadDecoder<InputEventT, DecoderError = DecoderErrorT> + Clone + Send + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-    InputEventT: Send,
-    EventHandlerT:
-        EventHandler<InputEvent = InputEventT, OutputEvent = OutputEventT, Error = HandlerErrorT>,
-    OutputEventT: ServiceMessage + Clone + Send + Sync + 'static,
-    HandlerErrorT: CheckedError + Debug + Send + Sync + 'static,
-    SerializerErrorT: Error + Debug + Send + Sync + 'static,
-    S3ClientT: S3 + Clone + Send + Sync + 'static,
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-    CompletionEventSerializerT: CompletionEventSerializer<
-        CompletedEvent = OutputEventT,
-        Output = Vec<u8>,
-        Error = SerializerErrorT,
-    >,
-{
-    loop {
-        tracing::trace!("Outer process loop");
-        let f = _process_loop(
-            queue_url.clone(),
-            dead_letter_queue_url.clone(),
-            cache,
-            sqs_client.clone(),
-            event_handler,
-            s3_payload_retriever,
-            s3_emitter,
-            serializer,
-            metric_reporter.clone(),
-        );
-        let f = AssertUnwindSafe(f);
-
-        if let Err(e) = f.catch_unwind().await {
-            if let Some(e) = e.downcast_ref::<Box<dyn std::error::Error + 'static>>() {
-                error!(
-                    queue_url = queue_url.as_str(),
-                    error = e.to_string().as_str(),
-                    "Processing loop panicked"
-                );
-            } else if let Some(e) = e.downcast_ref::<Box<dyn std::fmt::Debug>>() {
-                error!(
-                    error = format!("{:?}", e).as_str(),
-                    "Processing loop panicked"
-                );
-            } else {
-                error!("Unexpected error");
-            }
-            tokio::time::sleep(std::time::Duration::from_millis(200)).await;
-        }
-    }
-}
-
-async fn wait_loop<F, T, E>(max_tries: u32, f: impl Fn() -> F) -> Result<T, E>
-where
-    F: std::future::Future<Output = Result<T, E>>,
-    T: Send + Sync + 'static,
-    E: std::error::Error + Send + Sync + 'static,
-{
-    let mut errs = None;
-    for _ in 0..max_tries {
-        match (f)().await {
-            Ok(t) => return Ok(t),
-            Err(e) => {
-                errs = Some(Err(e));
-            }
-        };
-
-        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
-    }
-
-    if let Some(e) = errs {
-        e
-    } else {
-        unreachable!()
-    }
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum WaitForSqsError {
-    #[error("ListQueuesError")]
-    ListQueuesError(#[from] RusotoError<ListQueuesError>),
-    #[error("EmptyList")]
-    EmptyList,
-}
-
-pub async fn wait_for_sqs(
-    sqs_client: impl Sqs,
-    queue_name_prefix: impl Into<String>,
-) -> Result<(), WaitForSqsError> {
-    let queue_name_prefix = queue_name_prefix.into();
-    wait_loop(150, || async {
-        let list_res = sqs_client
-            .list_queues(ListQueuesRequest {
-                max_results: None,
-                next_token: None,
-                queue_name_prefix: Some(queue_name_prefix.clone()),
-            })
-            .await;
-        match list_res {
-            Err(e) => {
-                debug!("Waiting for sqs to become available: {:?}", e);
-                Err(WaitForSqsError::ListQueuesError(e))
-            }
-            Ok(res) => {
-                if let Some(res) = res.queue_urls {
-                    if res.is_empty() {
-                        return Err(WaitForSqsError::EmptyList);
-                    }
-                }
-                Ok(())
-            }
-        }
-    })
-    .await?;
-
-    Ok(())
-}
-
-pub fn time_based_key_fn(_event: &[u8]) -> String {
-    let cur_ms = match SystemTime::now().duration_since(UNIX_EPOCH) {
-        Ok(n) => n.as_millis(),
-        Err(_) => panic!("SystemTime before UNIX EPOCH!"),
-    };
-
-    let cur_day = cur_ms - (cur_ms % 86400);
-
-    format!("{}/{}-{}", cur_day, cur_ms, uuid::Uuid::new_v4())
-}
diff --git a/src/rust/sqs-executor/src/redis_cache.rs b/src/rust/sqs-executor/src/redis_cache.rs
deleted file mode 100644
index 657934a661..0000000000
--- a/src/rust/sqs-executor/src/redis_cache.rs
+++ /dev/null
@@ -1,342 +0,0 @@
-use std::{
-    io::Stdout,
-    time::Duration,
-};
-
-use async_trait::async_trait;
-use grapl_observe::{
-    metric_reporter::{
-        tag,
-        MetricReporter,
-    },
-    timers::TimedFutureExt,
-};
-use grapl_utils::future_ext::GraplFutureExt;
-use itertools::{
-    Either,
-    Itertools,
-};
-use lazy_static::lazy_static;
-use redis::{
-    AsyncCommands,
-    RedisError,
-};
-use tokio::time::error::Elapsed;
-
-use crate::{
-    cache::{
-        Cache,
-        Cacheable,
-    },
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-};
-
-lazy_static! {
-    /// Timeout for requests to Redis
-    static ref REDIS_REQUEST_TIMEOUT: Duration = Duration::from_millis(300);
-}
-
-/// Value we set in Redis to represent a "set" key
-const REDIS_SET_VALUE: &[u8; 1] = b"1";
-/// Value we set in LRU cache to represent a "set" key
-const LRU_SET_VALUE: () = ();
-
-#[derive(thiserror::Error, Debug)]
-pub enum RedisCacheError {
-    #[error("RedisError: {0}")]
-    RedisError(#[from] RedisError),
-    #[error("Cache Timeout")]
-    Timeout(#[from] Elapsed),
-    #[error("JoinError: {0}")]
-    JoinError(#[from] tokio::task::JoinError),
-}
-
-impl CheckedError for RedisCacheError {
-    fn error_type(&self) -> Recoverable {
-        Recoverable::Transient
-    }
-}
-
-#[derive(Clone)]
-pub struct RedisCache {
-    address: String,
-    connection_manager: redis::aio::ConnectionManager,
-    metric_reporter: MetricReporter<Stdout>,
-    lru_cache: Arc<Mutex<lru::LruCache<Vec<u8>, ()>>>,
-}
-
-impl RedisCache {
-    pub async fn new(
-        address: String,
-        metric_reporter: MetricReporter<Stdout>,
-    ) -> Result<Self, RedisError> {
-        RedisCache::with_lru_capacity(100_000, address, metric_reporter).await
-    }
-
-    pub async fn with_lru_capacity(
-        cap: usize,
-        address: String,
-        metric_reporter: MetricReporter<Stdout>,
-    ) -> Result<Self, RedisError> {
-        let client = redis::Client::open(address.clone())?;
-        let connection_manager = client.get_tokio_connection_manager().await?;
-
-        Ok(Self {
-            address,
-            connection_manager,
-            metric_reporter,
-            lru_cache: Arc::new(Mutex::new(lru::LruCache::new(cap))),
-        })
-    }
-}
-
-impl RedisCache {
-    async fn _filter_cached<CA>(&mut self, cacheables: &[CA]) -> Vec<CA>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        let get_identities = |cacheables: &Vec<CA>| -> Vec<Vec<u8>> {
-            cacheables.into_iter().map(|c| c.identity()).collect()
-        };
-
-        // Check LRU
-        let (lru_hits, lru_misses): (Vec<_>, Vec<_>) = {
-            let mut lru_cache = self.lru_cache.lock().unwrap();
-
-            cacheables
-                .iter()
-                .cloned()
-                .partition(|c| lru_cache.get(&c.identity()).is_some())
-        };
-
-        if !lru_hits.is_empty() {
-            info!(
-                "Event cache hits from LRU cache: {:?}",
-                get_identities(&lru_hits)
-            );
-        }
-
-        // If we have misses from the LRU cache, we check Redis.
-        // If lru_misses is empty we can return with empty vector
-        if lru_misses.is_empty() {
-            return lru_misses;
-        }
-
-        // Check Redis for misses from the LRU cache
-        let lru_miss_ids = get_identities(&lru_misses);
-        // connection_manager.get will return Option<T> if the input Vec has a single element,
-        // otherwise it returns a Vec<Option<T>>. We want to work with the Vec<Option<T>>.
-        let redis_result: Result<Vec<Option<u8>>, RedisError> = if lru_miss_ids.len() == 1 {
-            self.connection_manager
-                .get(lru_miss_ids.clone())
-                .await
-                .map(|value| vec![value])
-        } else {
-            self.connection_manager.get(lru_miss_ids.clone()).await
-        };
-
-        match redis_result {
-            Ok(responses) => {
-                let (redis_hits, redis_misses): (Vec<CA>, Vec<CA>) = lru_misses
-                    .into_iter()
-                    .zip(responses.into_iter())
-                    .partition_map(|(c, r)| match r {
-                        Some(_) => Either::Left(c),
-                        None => Either::Right(c),
-                    });
-
-                if !redis_hits.is_empty() {
-                    info!(
-                        "Event cache hits from Redis: {:?}",
-                        get_identities(&redis_hits)
-                    );
-                }
-
-                // If we have entries in redis_hits here it means they weren't found
-                // in the LRU cache. So we'll put them back in LRU cache since they
-                // were some of the latest seen
-                {
-                    let mut lru_cache = self.lru_cache.lock().unwrap();
-                    for cacheable in &redis_hits {
-                        // We don't care if identity already exists, so we discard the Option
-                        // returned from put.
-                        lru_cache.put(cacheable.identity(), LRU_SET_VALUE);
-                    }
-                }
-
-                // Return Redis cache missis, which should also be LRU cache misses
-                redis_misses
-            }
-            Err(e) => {
-                error!("{:?}", e);
-                // If Redis fails, we'll settle for returning LRU cache misses
-                lru_misses
-            }
-        }
-    }
-
-    #[tracing::instrument(skip(self, cacheable))]
-    async fn _store<CA>(&mut self, cacheable: CA) -> Result<(), RedisCacheError>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        self._store_all(&[cacheable]).await
-    }
-
-    #[tracing::instrument(skip(self, cacheables))]
-    async fn _store_all<CA>(&mut self, cacheables: &[CA]) -> Result<(), RedisCacheError>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        if cacheables.is_empty() {
-            return Ok(());
-        }
-
-        // Store cacheables in LRU and Redis caches
-
-        // We perform a slight optimization here: send to Redis only if the entry was not already
-        // in the LRU cache. If an entry exists in the LRU cache, then we must have previously
-        // stored it in the Redis cache as well, and we can avoid sending that to Redis again. As
-        // entries are evicted from the LRU cache, we'll just reset the value in the Redis cache.
-
-        // LRU PUT
-        let cacheables_for_redis: Vec<_> = {
-            let mut lru_cache = self.lru_cache.lock().unwrap();
-
-            cacheables
-                .iter()
-                .filter(|cacheable| lru_cache.put(cacheable.identity(), LRU_SET_VALUE).is_none())
-                .collect()
-        };
-
-        // Redis SET
-        let kv_pairs: Vec<(Vec<u8>, &[u8; 1])> = cacheables_for_redis
-            .into_iter()
-            .map(|k| (k.identity(), REDIS_SET_VALUE))
-            .collect();
-        self.connection_manager
-            .set_multiple(&kv_pairs)
-            .timeout(REDIS_REQUEST_TIMEOUT.clone())
-            .await??;
-
-        Ok(())
-    }
-}
-
-use std::sync::{
-    Arc,
-    Mutex,
-};
-
-use tracing::{
-    error,
-    info,
-};
-
-#[async_trait]
-impl Cache for RedisCache {
-    type CacheErrorT = RedisCacheError;
-
-    #[tracing::instrument(skip(self, cacheables))]
-    async fn all_exist<CA>(&mut self, cacheables: &[CA]) -> bool
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        let span = tracing::span!(
-            tracing::Level::DEBUG,
-            "redis_cache.all_exist",
-            address = self.address.as_str(),
-        );
-
-        let _enter = span.enter();
-        // Here we call filtered_cached to reuse the code for checking LRU and Redis caches
-        // If the response is empty then we know the entry is in the cache and we return true
-        let (res, ms) = self._filter_cached(cacheables).timed().await;
-
-        self.metric_reporter
-            .histogram(
-                "redis_cache.all_exist.ms",
-                ms as f64,
-                &[tag("success", true)],
-            )
-            .unwrap_or_else(|e| error!("failed to report redis_cache.all_exist.ms: {:?}", e));
-
-        // If the response is empty then we know the entry is in the cache and we return true
-        res.is_empty()
-    }
-
-    #[tracing::instrument(skip(self, cacheable))]
-    async fn store<CA>(&mut self, cacheable: CA) -> Result<(), Self::CacheErrorT>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        let span = tracing::span!(
-            tracing::Level::DEBUG,
-            "redis_cache.store",
-            address = self.address.as_str(),
-        );
-
-        let _enter = span.enter();
-        let (res, ms) = self._store(cacheable).timed().await;
-        self.metric_reporter
-            .histogram(
-                "redis_cache.store.ms",
-                ms as f64,
-                &[tag("success", res.is_ok())],
-            )
-            .unwrap_or_else(|e| error!("failed to report redis_cache.put.ms: {:?}", e));
-        res
-    }
-
-    #[tracing::instrument(skip(self, cacheables))]
-    async fn store_all<CA>(&mut self, cacheables: &[CA]) -> Result<(), Self::CacheErrorT>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        let span = tracing::span!(
-            tracing::Level::TRACE,
-            "redis_cache.store_all",
-            address = self.address.as_str(),
-            cacheables_len = cacheables.len(),
-        );
-        let _enter = span.enter();
-        let (res, ms) = self._store_all(cacheables).timed().await;
-        self.metric_reporter
-            .histogram(
-                "redis_cache.store_all.ms",
-                ms as f64,
-                &[tag("success", res.is_ok())],
-            )
-            .unwrap_or_else(|e| error!("failed to report redis_cache.put_all.ms: {:?}", e));
-        res
-    }
-
-    #[tracing::instrument(skip(self, cacheables))]
-    async fn filter_cached<CA>(&mut self, cacheables: &[CA]) -> Vec<CA>
-    where
-        CA: Cacheable + Send + Sync + Clone + 'static,
-    {
-        let span = tracing::span!(
-            tracing::Level::TRACE,
-            "redis_cache.filter_cached",
-            address = self.address.as_str(),
-            cacheables_len = cacheables.len(),
-        );
-        let _enter = span.enter();
-
-        let (res, ms) = self._filter_cached(cacheables).timed().await;
-
-        self.metric_reporter
-            .histogram(
-                "redis_cache.filter_cached.ms",
-                ms as f64,
-                &[tag("success", true)],
-            )
-            .unwrap_or_else(|e| error!("failed to report redis_cache.filter_cached.ms: {:?}", e));
-
-        res
-    }
-}
diff --git a/src/rust/sqs-executor/src/retriever/event_retriever.rs b/src/rust/sqs-executor/src/retriever/event_retriever.rs
deleted file mode 100644
index 5d15e641ef..0000000000
--- a/src/rust/sqs-executor/src/retriever/event_retriever.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use async_trait::async_trait;
-use rust_proto::pipeline::Metadata;
-
-use crate::errors::CheckedError;
-
-#[async_trait]
-pub trait PayloadRetriever<T> {
-    type Message;
-    type Error: CheckedError;
-    async fn retrieve_event(
-        &mut self,
-        msg: &Self::Message,
-    ) -> Result<Option<(Metadata, T)>, Self::Error>;
-}
diff --git a/src/rust/sqs-executor/src/retriever/mod.rs b/src/rust/sqs-executor/src/retriever/mod.rs
deleted file mode 100644
index bc0f0ae7c2..0000000000
--- a/src/rust/sqs-executor/src/retriever/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-pub mod event_retriever;
-pub mod s3_event_retriever;
-
-pub use event_retriever::PayloadRetriever;
-pub use s3_event_retriever::S3PayloadRetriever;
diff --git a/src/rust/sqs-executor/src/retriever/s3_event_retriever.rs b/src/rust/sqs-executor/src/retriever/s3_event_retriever.rs
deleted file mode 100644
index 6224600303..0000000000
--- a/src/rust/sqs-executor/src/retriever/s3_event_retriever.rs
+++ /dev/null
@@ -1,272 +0,0 @@
-use std::{
-    collections::HashMap,
-    io::Stdout,
-    marker::PhantomData,
-    time::Duration,
-};
-
-use async_trait::async_trait;
-use futures::FutureExt;
-use grapl_observe::{
-    metric_reporter::{
-        tag,
-        HistogramUnit,
-        MetricReporter,
-    },
-    timers::{
-        time_it,
-        TimedFutureExt,
-    },
-};
-use rusoto_core::RusotoError;
-use rusoto_s3::{
-    GetObjectError,
-    GetObjectRequest,
-    S3,
-};
-use rusoto_sqs::Message as SqsMessage;
-use rust_proto::pipeline::Metadata;
-use tokio::{
-    io::AsyncReadExt,
-    time::error::Elapsed,
-};
-use tracing::{
-    debug,
-    error,
-};
-
-use crate::{
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-    event_decoder::PayloadDecoder,
-    PayloadRetriever,
-};
-
-pub struct S3PayloadRetriever<S, SInit, D, E, DecoderErrorT>
-where
-    S: S3 + Clone + Send + Sync + 'static,
-    SInit: (Fn(String) -> S) + Clone + Send + Sync + 'static,
-    D: PayloadDecoder<E, DecoderError = DecoderErrorT> + Clone + Send + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-    E: Send + 'static,
-{
-    s3_init: SInit,
-    s3_clients: HashMap<String, S>,
-    decoder: D,
-    metric_reporter: MetricReporter<Stdout>,
-    phantom: PhantomData<(E, DecoderErrorT)>,
-}
-
-impl<S, SInit, D, E, DecoderErrorT> Clone for S3PayloadRetriever<S, SInit, D, E, DecoderErrorT>
-where
-    S: S3 + Clone + Send + Sync + 'static,
-    SInit: (Fn(String) -> S) + Clone + Send + Sync + 'static,
-    D: PayloadDecoder<E, DecoderError = DecoderErrorT> + Clone + Send + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-    E: Send + 'static,
-{
-    fn clone(&self) -> Self {
-        Self {
-            s3_init: self.s3_init.clone(),
-            s3_clients: self.s3_clients.clone(),
-            decoder: self.decoder.clone(),
-            metric_reporter: self.metric_reporter.clone(),
-            phantom: std::marker::PhantomData,
-        }
-    }
-}
-
-impl<S, SInit, D, E, DecoderErrorT> S3PayloadRetriever<S, SInit, D, E, DecoderErrorT>
-where
-    S: S3 + Clone + Send + Sync + 'static,
-    SInit: (Fn(String) -> S) + Clone + Send + Sync + 'static,
-    D: PayloadDecoder<E, DecoderError = DecoderErrorT> + Clone + Send + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-    E: Send + 'static,
-{
-    pub fn new(s3: SInit, decoder: D, metric_reporter: MetricReporter<Stdout>) -> Self {
-        Self {
-            s3_init: s3,
-            s3_clients: HashMap::new(),
-            decoder,
-            metric_reporter,
-            phantom: PhantomData,
-        }
-    }
-
-    pub fn get_client(&mut self, region: String) -> S {
-        match self.s3_clients.get(&region) {
-            Some(s3) => s3.clone(),
-            None => {
-                let client = (self.s3_init)(region.clone());
-                self.s3_clients.insert(region.to_string(), client.clone());
-                client
-            }
-        }
-    }
-}
-
-// PayloadDecoder
-
-#[derive(thiserror::Error, Debug)]
-pub enum S3PayloadRetrieverError<DecoderErrorT>
-where
-    DecoderErrorT: CheckedError + 'static,
-{
-    #[error("S3Error: {0}")]
-    S3Error(#[from] RusotoError<GetObjectError>),
-    #[error("Decode error {0}")]
-    DecodeError(#[from] DecoderErrorT),
-    #[error("EnvelopeDecode error {0}")]
-    EnvelopeDecode(#[from] prost::DecodeError),
-    #[error("IO {0}")]
-    Io(#[from] std::io::Error),
-    #[error("JSON {0}")]
-    Json(#[from] serde_json::Error),
-    #[error("Timeout {0}")]
-    Timeout(#[from] Elapsed),
-}
-
-impl<DecoderErrorT> CheckedError for S3PayloadRetrieverError<DecoderErrorT>
-where
-    DecoderErrorT: CheckedError + 'static,
-{
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::S3Error(_) => Recoverable::Transient,
-            Self::DecodeError(_) => Recoverable::Persistent,
-            Self::EnvelopeDecode(_) => Recoverable::Persistent,
-            Self::Io(_) => Recoverable::Transient,
-            Self::Json(_) => Recoverable::Persistent,
-            Self::Timeout(_) => Recoverable::Transient,
-        }
-    }
-}
-
-#[async_trait]
-impl<S, SInit, D, E, DecoderErrorT> PayloadRetriever<E>
-    for S3PayloadRetriever<S, SInit, D, E, DecoderErrorT>
-where
-    S: S3 + Clone + Send + Sync + 'static,
-    SInit: (Fn(String) -> S) + Clone + Send + Sync + 'static,
-    D: PayloadDecoder<E, DecoderError = DecoderErrorT> + Clone + Send + 'static,
-    E: Send + 'static,
-    DecoderErrorT: CheckedError + Send + 'static,
-{
-    type Message = SqsMessage;
-    type Error = S3PayloadRetrieverError<DecoderErrorT>;
-
-    #[tracing::instrument(skip(self, msg))]
-    async fn retrieve_event(
-        &mut self,
-        msg: &Self::Message,
-    ) -> Result<Option<(Metadata, E)>, Self::Error> {
-        let body = msg.body.as_ref().unwrap();
-        debug!("Got body from message: {}", body);
-        let event: serde_json::Value = serde_json::from_str(body)?;
-
-        if let Some(Some(event_str)) = event.get("Event").map(serde_json::Value::as_str) {
-            if event_str == "s3:TestEvent" {
-                return Ok(None);
-            }
-        }
-        let record = &event["Records"][0]["s3"];
-
-        let bucket = record["bucket"]["name"].as_str().expect("bucket name");
-        let key = record["object"]["key"].as_str().expect("object key");
-
-        let inner_loop_span = tracing::trace_span!(
-            "s3.retrieve_event",
-            bucket=?bucket,
-            key=?key,
-        );
-        let _enter = inner_loop_span.enter();
-
-        debug!(message = "Retrieving S3 payload",);
-
-        let region = &event["Records"][0]["awsRegion"].as_str().expect("region");
-        let s3 = self.get_client(region.to_string());
-        let s3_data = s3.get_object(GetObjectRequest {
-            bucket: bucket.to_string(),
-            key: key.to_string(),
-            ..Default::default()
-        });
-
-        let s3_data = tokio::time::timeout(Duration::from_secs(3), s3_data);
-        let s3_data = s3_data
-            .timed()
-            .map(|(s3_data, ms)| {
-                self.metric_reporter
-                    .histogram(
-                        "s3_consumer.get_object.ms",
-                        ms as f64,
-                        &[tag("success", s3_data.is_ok())],
-                    )
-                    .unwrap_or_else(
-                        |e| error!(message="failed to report s3_consumer.get_object.ms", error=?e),
-                    );
-                s3_data
-            })
-            .await??;
-
-        let object_size = record["object"]["size"].as_u64().unwrap_or_default();
-        let prealloc = if object_size < 1024 {
-            1024
-        } else {
-            object_size as usize
-        };
-
-        debug!(message="Retrieved s3 payload", object_size=?object_size);
-
-        let mut body = Vec::with_capacity(prealloc);
-
-        s3_data
-            .body
-            .expect("Missing S3 body")
-            .into_async_read()
-            .read_to_end(&mut body)
-            .timed()
-            .map(|(res, ms)| {
-                self.metric_reporter
-                    .histogram(
-                        "s3_consumer.read_to_end.ms",
-                        ms as f64,
-                        &[tag("success", res.is_ok())],
-                    )
-                    .unwrap_or_else(
-                        |e| error!(message="failed to report s3_consumer.read_to_end.ms", error=?e),
-                    );
-                res
-            })
-            .await?;
-
-        self.metric_reporter
-            .gauge("s3_retriever.bytes", body.len() as f64, &[])
-            .unwrap_or_else(|e| error!(message="failed to report s3_retriever.bytes", error=?e));
-
-        debug!("Read s3 payload body");
-
-        let envelope: rust_proto::pipeline::Envelope = prost::Message::decode(&body[..])?;
-        let meta = envelope
-            .metadata
-            .expect("Metadata must be set at the front of the pipeline");
-        let body = envelope.inner_message;
-
-        let (decoded, ms) = time_it(|| self.decoder.decode(body));
-
-        self.metric_reporter
-            .histogram_with_units(
-                "s3_retriever.decoded.micros",
-                ms.as_micros() as f64,
-                HistogramUnit::Micros,
-                &[tag("success", true)][..],
-            )
-            .unwrap_or_else(
-                |e| error!(message="failed to report s3_retriever.decoded.micros", error=?e),
-            );
-
-        Ok(Some((meta, decoded?)))
-    }
-}
diff --git a/src/rust/sqs-executor/src/rusoto_helpers.rs b/src/rust/sqs-executor/src/rusoto_helpers.rs
deleted file mode 100644
index 4cffbcf1c4..0000000000
--- a/src/rust/sqs-executor/src/rusoto_helpers.rs
+++ /dev/null
@@ -1,351 +0,0 @@
-use std::io::Stdout;
-
-use grapl_observe::{
-    metric_reporter::{
-        tag,
-        MetricReporter,
-    },
-    timers::{
-        time_fut_ms,
-        TimedFutureExt,
-    },
-};
-use rusoto_core::RusotoError;
-use rusoto_s3::PutObjectError as InnerPutObjectError;
-use rusoto_sqs::{
-    DeleteMessageError as InnerDeleteMessageError,
-    DeleteMessageRequest,
-    Message as SqsMessage,
-    ReceiveMessageError as InnerReceiveMessageError,
-    ReceiveMessageRequest,
-    SendMessageRequest,
-    Sqs,
-};
-use tokio::{
-    task::{
-        JoinError,
-        JoinHandle,
-    },
-    time::error::Elapsed,
-};
-use tracing::{
-    debug,
-    error,
-    Instrument,
-};
-
-use crate::errors::{
-    CheckedError,
-    Recoverable,
-};
-
-impl CheckedError for InnerDeleteMessageError {
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::InvalidIdFormat(_) => Recoverable::Persistent,
-            Self::ReceiptHandleIsInvalid(_) => Recoverable::Persistent,
-        }
-    }
-}
-
-// PutObjectError has no variants, and conveys no information
-// about what went wrong, so we must assume a transient error
-impl CheckedError for InnerPutObjectError {
-    fn error_type(&self) -> Recoverable {
-        Recoverable::Transient
-    }
-}
-
-impl<E> CheckedError for RusotoError<E>
-where
-    E: CheckedError + 'static,
-{
-    /// In all cases, other than Service there's no way to inspect the error
-    /// since it's just a String, so we default to assuming it's transient, even though
-    /// that may not always be the case.
-    fn error_type(&self) -> Recoverable {
-        match self {
-            RusotoError::Service(e) => e.error_type(),
-            RusotoError::HttpDispatch(_)
-            | RusotoError::Credentials(_)
-            | RusotoError::Validation(_)
-            | RusotoError::ParseError(_)
-            | RusotoError::Unknown(_)
-            | RusotoError::Blocking => Recoverable::Transient,
-        }
-    }
-}
-
-pub async fn get_message<SqsT>(
-    queue_url: String,
-    sqs_client: SqsT,
-    metric_reporter: &mut MetricReporter<Stdout>,
-) -> Result<Vec<SqsMessage>, RusotoError<InnerReceiveMessageError>>
-where
-    SqsT: Sqs + Clone + Send + Sync + 'static,
-{
-    const WAIT_TIME_SECONDS: i64 = 20;
-    let messages = sqs_client.receive_message(ReceiveMessageRequest {
-        max_number_of_messages: Some(10),
-        queue_url,
-        visibility_timeout: Some(30),
-        wait_time_seconds: Some(WAIT_TIME_SECONDS),
-        ..Default::default()
-    });
-
-    let messages = tokio::time::timeout(
-        std::time::Duration::from_secs((WAIT_TIME_SECONDS as u64) + 1),
-        messages,
-    );
-    let (messages, ms) = time_fut_ms(messages).await;
-
-    let messages = messages
-        .expect("timeout")
-        .map(|m| m.messages.unwrap_or_else(|| vec![]));
-
-    if let Ok(ref msgs) = messages {
-        metric_reporter
-            .histogram(
-                "sqs_executor.receive_message",
-                ms as f64,
-                &[tag("success", true), tag("empty_receive", msgs.is_empty())][..],
-            )
-            .unwrap_or_else(|e| {
-                error!(
-                    error = e.to_string().as_str(),
-                    metric_name = "sqs_executor.receive_message",
-                    "Failed to report histogram metric",
-                )
-            });
-    } else {
-        metric_reporter
-            .histogram(
-                "sqs_executor.receive_message",
-                ms as f64,
-                &[tag("success", false)],
-            )
-            .unwrap_or_else(|e| {
-                error!(
-                    error = e.to_string().as_str(),
-                    metric_name = "sqs_executor.receive_message",
-                    "Failed to report histogram metric",
-                )
-            });
-    };
-
-    messages
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum SendMessageError {
-    #[error("SendMessageError {0}")]
-    InnerSendMessageError(#[from] RusotoError<rusoto_sqs::SendMessageError>),
-    #[error("SendMessage Timeout")]
-    Timeout(#[from] Elapsed),
-}
-
-impl CheckedError for rusoto_sqs::SendMessageError {
-    fn error_type(&self) -> Recoverable {
-        Recoverable::Persistent
-    }
-}
-
-impl CheckedError for SendMessageError {
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::InnerSendMessageError(e) => e.error_type(),
-            _ => Recoverable::Transient,
-        }
-    }
-}
-
-pub fn send_message<SqsT>(
-    queue_url: String,
-    message_body: String,
-    sqs_client: SqsT,
-    mut metric_reporter: MetricReporter<Stdout>,
-) -> JoinHandle<Result<(), SendMessageError>>
-where
-    SqsT: Sqs + Clone + Send + Sync + 'static,
-{
-    tokio::task::spawn(async move {
-        let metric_reporter = &mut metric_reporter;
-        let mut last_err = None;
-        for i in 0..5u64 {
-            let sqs_client = sqs_client.clone();
-            let res = sqs_client.send_message(SendMessageRequest {
-                queue_url: queue_url.clone(),
-                message_body: message_body.clone(),
-                ..Default::default()
-            });
-
-            let res = tokio::time::timeout(std::time::Duration::from_secs(21), res)
-                .timed()
-                .await;
-
-            match res {
-                (Ok(Ok(_)), ms) => {
-                    metric_reporter
-                        .histogram(
-                            "sqs_executor.send_message.ms",
-                            ms as f64,
-                            &[tag("success", true)],
-                        )
-                        .unwrap_or_else(|e| {
-                            error!(
-                                error = e.to_string().as_str(),
-                                metric_name = "sqs_executor.send_message",
-                                "Failed to report histogram metric",
-                            )
-                        });
-                    debug!("Send message: {}", queue_url.clone());
-                    return Ok(());
-                }
-                (Ok(Err(e)), ms) => {
-                    metric_reporter
-                        .histogram(
-                            "sqs_executor.send_message.ms",
-                            ms as f64,
-                            &[tag("success", false)],
-                        )
-                        .unwrap_or_else(|e| {
-                            error!(
-                                error = e.to_string().as_str(),
-                                metric_name = "sqs_executor.send_message",
-                                "Failed to report histogram metric",
-                            )
-                        });
-
-                    if let Recoverable::Persistent = e.error_type() {
-                        return Err(SendMessageError::from(e));
-                    } else {
-                        last_err = Some(SendMessageError::from(e));
-                        tokio::time::sleep(std::time::Duration::from_millis(10 * i)).await;
-                    }
-                }
-                (Err(e), ms) => {
-                    metric_reporter
-                        .histogram(
-                            "sqs_executor.send_message.ms",
-                            ms as f64,
-                            &[tag("success", false)],
-                        )
-                        .unwrap_or_else(|e| {
-                            error!(
-                                error = e.to_string().as_str(),
-                                metric_name = "sqs_executor.send_message",
-                                "Failed to report histogram metric with timeout",
-                            )
-                        });
-
-                    last_err = Some(SendMessageError::from(e));
-                    tokio::time::sleep(std::time::Duration::from_millis(10 * i)).await;
-                }
-            }
-        }
-        Err(last_err.unwrap())
-    })
-}
-
-#[tracing::instrument(skip(sqs_client, queue_url, receipt_handle, metric_reporter))]
-pub fn delete_message<SqsT>(
-    sqs_client: SqsT,
-    queue_url: String,
-    receipt_handle: String,
-    mut metric_reporter: MetricReporter<Stdout>,
-) -> tokio::task::JoinHandle<()>
-where
-    SqsT: Sqs + Clone + Send + Sync + 'static,
-{
-    let delete_f = async move {
-        let metric_reporter = &mut metric_reporter;
-        for _ in 0..5u8 {
-            match sqs_client
-                .clone()
-                .delete_message(DeleteMessageRequest {
-                    queue_url: queue_url.clone(),
-                    receipt_handle: receipt_handle.clone(),
-                })
-                .timed()
-                .await
-            {
-                (Ok(_), ms) => {
-                    metric_reporter
-                        .histogram(
-                            "sqs_executor.delete_message.ms",
-                            ms as f64,
-                            &[tag("success", true)],
-                        )
-                        .unwrap_or_else(|e| error!("sqs_executor.delete_message.ms: {:?}", e));
-                    debug!("Deleted message: {}", receipt_handle.clone());
-                    return;
-                }
-                (Err(e), ms) => {
-                    metric_reporter
-                        .histogram(
-                            "sqs_executor.delete_message.ms",
-                            ms as f64,
-                            &[tag("success", false)],
-                        )
-                        .unwrap_or_else(|e| error!("sqs_executor.delete_message.ms: {:?}", e));
-                    error!(
-                        "Failed to delete_message with: {:?} {:?}",
-                        e,
-                        e.error_type()
-                    );
-                    if let Recoverable::Persistent = e.error_type() {
-                        return;
-                    }
-                }
-            }
-        }
-    };
-    tokio::task::spawn(delete_f.in_current_span())
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum MoveToDeadLetterError {
-    #[error("SerializeError {0}")]
-    SerializeError(#[from] serde_json::Error),
-    #[error("SendMessageError {0}")]
-    SendMessageError(#[from] SendMessageError),
-    #[error("DeleteMessageError {0}")]
-    DeleteMessageError(#[from] InnerDeleteMessageError),
-    #[error("JoinError {0}")]
-    JoinError(#[from] JoinError),
-}
-
-pub async fn move_to_dead_letter<SqsT>(
-    sqs_client: SqsT,
-    message: &impl serde::Serialize,
-    publish_to_queue: String,
-    delete_from_queue: String,
-    receipt_handle: String,
-    metric_reporter: MetricReporter<Stdout>,
-) -> Result<(), MoveToDeadLetterError>
-where
-    SqsT: Sqs + Clone + Send + Sync + 'static,
-{
-    debug!(
-        publish_to_queue = publish_to_queue.as_str(),
-        delete_from_queue = delete_from_queue.as_str(),
-        "Moving message to deadletter queue"
-    );
-    let message = serde_json::to_string(&message);
-    let message = message?;
-    send_message(
-        publish_to_queue,
-        message,
-        sqs_client.clone(),
-        metric_reporter.clone(),
-    )
-    .await??;
-    delete_message(
-        sqs_client,
-        delete_from_queue,
-        receipt_handle,
-        metric_reporter,
-    )
-    .await?;
-    Ok(())
-}
diff --git a/src/rust/sqs-executor/src/s3_event_emitter.rs b/src/rust/sqs-executor/src/s3_event_emitter.rs
deleted file mode 100644
index 2571a2143e..0000000000
--- a/src/rust/sqs-executor/src/s3_event_emitter.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-use std::io::Stdout;
-
-use async_trait::async_trait;
-use grapl_observe::{
-    metric_reporter::{
-        tag,
-        MetricReporter,
-    },
-    timers::TimedFutureExt,
-};
-use rusoto_core::RusotoError;
-use rusoto_s3::{
-    PutObjectError,
-    PutObjectRequest,
-    S3,
-};
-use rusoto_sqs::SendMessageError;
-use tokio::time::{
-    error::Elapsed,
-    Duration,
-};
-use tracing::error;
-
-use crate::{
-    errors::{
-        CheckedError,
-        Recoverable,
-    },
-    event_emitter::EventEmitter,
-};
-
-#[derive(thiserror::Error, Debug)]
-pub enum S3EventEmitterError {
-    #[error("PutObjectError: {0}")]
-    PutObjectError(#[from] RusotoError<PutObjectError>),
-    #[error("Timeout")]
-    Timeout(#[from] Elapsed),
-}
-
-pub struct S3EventEmitter<S, F>
-where
-    S: Clone + S3 + Send + Sync,
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-{
-    s3: S,
-    output_bucket: String,
-    key_fn: F,
-    metric_reporter: MetricReporter<Stdout>,
-}
-
-impl<S, F> Clone for S3EventEmitter<S, F>
-where
-    S: Clone + S3 + Send + Sync,
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-{
-    fn clone(&self) -> Self {
-        Self {
-            s3: self.s3.clone(),
-            output_bucket: self.output_bucket.clone(),
-            key_fn: self.key_fn.clone(),
-            metric_reporter: self.metric_reporter.clone(),
-        }
-    }
-}
-
-impl<S, F> S3EventEmitter<S, F>
-where
-    S: Clone + S3 + Send + Sync,
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-{
-    pub fn new(
-        s3: S,
-        output_bucket: impl Into<String>,
-        key_fn: F,
-        metric_reporter: MetricReporter<Stdout>,
-    ) -> Self {
-        let output_bucket = output_bucket.into();
-        Self {
-            s3,
-            output_bucket,
-            key_fn,
-            metric_reporter,
-        }
-    }
-}
-
-#[async_trait]
-impl<S, F> EventEmitter for S3EventEmitter<S, F>
-where
-    S: Clone + S3 + Send + Sync,
-    F: Clone + Fn(&[u8]) -> String + Send + Sync + 'static,
-{
-    type Event = Vec<u8>;
-    type Error = S3EventEmitterError;
-
-    #[tracing::instrument(skip(self, events))]
-    async fn emit_event(&mut self, events: Vec<Self::Event>) -> Result<(), Self::Error> {
-        let mut event_uploads = Vec::with_capacity(events.len());
-
-        for event in events {
-            let output_bucket = self.output_bucket.clone();
-            let key = (self.key_fn)(&event);
-            let s3 = self.s3.clone();
-            let mut metric_reporter = self.metric_reporter.clone();
-            let put_object = async move {
-                tracing::info!("uploading event to: {} {}", output_bucket, key);
-                let (res, ms) = s3
-                    .put_object(PutObjectRequest {
-                        body: Some(event.into()),
-                        bucket: output_bucket.clone(),
-                        key: key.clone(),
-                        ..Default::default()
-                    })
-                    .timed()
-                    .await;
-                metric_reporter
-                    .histogram(
-                        "s3_event_emitter.emit_event.ms",
-                        ms as f64,
-                        &[tag("success", res.is_ok())],
-                    )
-                    .unwrap_or_else(|e| error!("s3_event_emitter.emit_event.ms: {:?}", e));
-
-                match res {
-                    Ok(res) => {
-                        tracing::debug!("PutObject succeeded: {:?}", res);
-                        Ok(())
-                    }
-                    Err(e) => {
-                        tracing::warn!("PutObject failed: {:?}", e);
-                        Err(S3EventEmitterError::from(e))
-                    }
-                }
-            };
-            event_uploads.push(put_object);
-        }
-        let event_uploads = tokio::time::timeout(
-            Duration::from_secs(3),
-            futures::future::join_all(event_uploads),
-        )
-        .await?;
-
-        for e in event_uploads.into_iter() {
-            e?;
-        }
-        Ok(())
-    }
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum S3NotificationError {
-    #[error("PublishError")]
-    PublishError(#[from] RusotoError<SendMessageError>),
-}
-
-impl CheckedError for S3NotificationError {
-    fn error_type(&self) -> Recoverable {
-        match self {
-            Self::PublishError(e) => e.error_type(),
-        }
-    }
-}
diff --git a/src/rust/sqs-executor/src/sqs_timeout_manager.rs b/src/rust/sqs-executor/src/sqs_timeout_manager.rs
deleted file mode 100644
index ef38b5bdc4..0000000000
--- a/src/rust/sqs-executor/src/sqs_timeout_manager.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-use std::time::{
-    Duration,
-    Instant,
-};
-
-use futures::{
-    future::{
-        self,
-        Either,
-    },
-    pin_mut,
-};
-use rusoto_sqs::{
-    ChangeMessageVisibilityRequest,
-    Sqs,
-};
-use tokio::sync::{
-    mpsc::{
-        channel as mpsc_channel,
-        Receiver as MpscReceiver,
-        Sender as MpscSender,
-    },
-    oneshot::{
-        channel as one_shot,
-        Receiver as OneShotReceiver,
-        Sender as OneShotSender,
-    },
-};
-use tracing_futures::Instrument;
-
-struct SqsTimeoutManager<S>
-where
-    S: Sqs + Send + Sync + 'static,
-{
-    queue_url: String,
-    receipt_handle: String,
-    message_id: String,
-    visibility_timeout: u64,
-    receiver: MpscReceiver<()>,
-    s: S,
-}
-
-impl<S> SqsTimeoutManager<S>
-where
-    S: Sqs + Send + Sync + 'static,
-{
-    async fn start(self) {
-        let Self {
-            queue_url,
-            receipt_handle,
-            message_id,
-            visibility_timeout,
-            mut receiver,
-            s,
-        } = self;
-        tracing::info!("Starting keep_alive for message");
-
-        let start = Instant::now();
-
-        // Sleep for N - 10 seconds, set timeout to 2N
-        let message_id = &message_id;
-        let max_iter = 10;
-        for i in 1..=max_iter {
-            let timeout_fut = async {
-                tokio::time::sleep(Duration::from_secs(get_next_sleep_for(
-                    visibility_timeout,
-                    i,
-                )))
-                .await
-            };
-            let future_2 = async { receiver.recv().await };
-            pin_mut!(timeout_fut);
-            pin_mut!(future_2);
-            match future::select(timeout_fut, future_2).await {
-                Either::Left(_) => {
-                    let res = s
-                        .change_message_visibility(ChangeMessageVisibilityRequest {
-                            queue_url: queue_url.clone(),
-                            receipt_handle: receipt_handle.clone(),
-                            visibility_timeout: get_next_timeout(visibility_timeout, i),
-                        })
-                        .await;
-
-                    match res {
-                        Ok(()) => {
-                            tracing::debug!(
-                                iteration = i,
-                                message_id = message_id.as_str(),
-                                receipt_handle = receipt_handle.as_str(),
-                                time_taken = start.elapsed().as_millis() as u64,
-                                "Successfully changed message visibility"
-                            );
-                        }
-                        Err(rusoto_core::RusotoError::Service(e)) => {
-                            tracing::error!(
-                                error=?e,
-                                iteration=i,
-                                message_id=message_id.as_str(),
-                                receipt_handle=receipt_handle.as_str(),
-                                time_taken=start.elapsed().as_millis() as u64,
-                                "Failed to change message visibility"
-                            );
-                            break; // These errors are not retryable
-                        }
-                        Err(e) => {
-                            tracing::warn!(
-                                error = e.to_string().as_str(),
-                                iteration = i,
-                                message_id = message_id.as_str(),
-                                receipt_handle = receipt_handle.as_str(),
-                                time_taken = start.elapsed().as_millis() as u64,
-                                "Failed to change message visibility, but it's probably fine"
-                            );
-                            return;
-                        }
-                    };
-                }
-                Either::Right(_) => {
-                    tracing::debug!(
-                        iteration = i,
-                        message_id = message_id.as_str(),
-                        receipt_handle = receipt_handle.as_str(),
-                        time_taken = start.elapsed().as_millis() as u64,
-                        "Message no longer needs to be kept alive"
-                    );
-                    return;
-                }
-            };
-
-            tracing::debug!(
-                iteration = i,
-                message_id = message_id.as_str(),
-                receipt_handle = receipt_handle.as_str(),
-                time_taken = start.elapsed().as_millis() as u64,
-                "message-visibility-loop",
-            );
-        }
-
-        tracing::warn!(
-            iteration = max_iter,
-            message_id = message_id.as_str(),
-            receipt_handle = receipt_handle.as_str(),
-            time_taken = start.elapsed().as_millis() as u64,
-            "message still has not processed"
-        );
-    }
-}
-
-fn get_next_sleep_for(initial_timeout: u64, i: u64) -> u64 {
-    (initial_timeout * i) - 10
-}
-
-fn get_next_timeout(initial_timeout: u64, i: u64) -> i64 {
-    (initial_timeout * (i + 1)) as i64
-}
-
-// Provides a wrapper for a OneShot to communicate with an Mpsc. This is a hack
-// to work around async destructors.
-// todo: investigate https://docs.rs/tokio/1.0.1/tokio/sync/mpsc/struct.Sender.html#method.blocking_send
-async fn route_oneshot(source_queue: OneShotReceiver<()>, dest_queue: MpscSender<()>) {
-    let _ = source_queue.await;
-    let _ = dest_queue.send(()).await;
-}
-
-// Do not impl Clone on this. Use an Arc if you need to clone.
-/// Sends a message one time, either explicitly via `stop` or implicitly via `Drop`
-pub struct Sender {
-    sender: Option<OneShotSender<()>>,
-}
-
-impl Sender {
-    pub fn stop(self) {}
-}
-
-impl Drop for Sender {
-    fn drop(&mut self) {
-        if let Some(sender) = self.sender.take() {
-            let _ = sender.send(()).map_err(|()| {
-                tracing::error!("Attempting to drop queue sender, but channel was closed.")
-            });
-        }
-    }
-}
-
-/// Given a message receipt, a queue, and a timeout, `keep_alive` will ensure
-/// that a message will stay alive during the lifetime of the returned `Sender`.
-pub fn keep_alive<S>(
-    s: S,
-    receipt_handle: String,
-    message_id: String,
-    queue_url: String,
-    visibility_timeout: u64,
-) -> Sender
-where
-    S: Sqs + Send + Sync + 'static,
-{
-    let (os_tx, os_rx) = one_shot();
-    let (mpsc_tx, mpsc_rx) = mpsc_channel(1);
-
-    let span = tracing::span!(
-        tracing::Level::INFO,
-        "keep_alive",
-        receipt_handle = receipt_handle.as_str(),
-        message_id = message_id.as_str(),
-        queue_url = queue_url.as_str(),
-    );
-    let _enter = span.enter();
-    let start_f = async move {
-        let manager = SqsTimeoutManager {
-            queue_url,
-            receipt_handle,
-            message_id,
-            visibility_timeout,
-            receiver: mpsc_rx,
-            s,
-        };
-        manager.start().await;
-    }
-    .in_current_span();
-    tokio::task::spawn(start_f);
-
-    let route_f = async move {
-        route_oneshot(os_rx, mpsc_tx).await;
-    }
-    .in_current_span();
-    tokio::task::spawn(route_f);
-
-    Sender {
-        sender: Some(os_tx),
-    }
-}


Test patch:
diff --git a/.buildkite/pipeline.testing.yml b/.buildkite/pipeline.testing.yml
index a81999cd77..2bf6356c5c 100644
--- a/.buildkite/pipeline.testing.yml
+++ b/.buildkite/pipeline.testing.yml
@@ -19,6 +19,6 @@ steps:
           post-command: |
             .buildkite/scripts/grapl_testing_stack/dump_artifacts.sh
     command:
-      - .buildkite/scripts/grapl_testing_stack/run_parameterized_job.sh integration-tests-new 6
+      - .buildkite/scripts/grapl_testing_stack/run_parameterized_job.sh rust-integration-tests 6
     artifact_paths:
       - "test_artifacts/**/*"
diff --git a/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow_test.py b/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow_test.py
index f859360888..3bc35db9c4 100644
--- a/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow_test.py
+++ b/etc/ci_scripts/dump_artifacts/analysis/pipeline_message_flow_test.py
@@ -19,20 +19,3 @@ def test_receive_count_sysmon_generator() -> None:
     input = TEST_INPUT_FROM_SYSMON_GENERATOR
     receive_count = get_num_received_messages(input)
     assert receive_count == 4
-
-
-TEST_INPUT_FROM_ANALYZER_EXECUTOR = """
-SQS MessageID 0fa16960-befd-42c1-ae2c-f5be7aca89f8: Loop 1 - waiting 20s for task
-Writing out plugins to: /tmp/model_plugins
-Executing Analyzer: analyzers/suspicious_svchost/main.py
-MONITORING|analyzer-executor|2022-04-21T18:03:14.184+00:00|analyzer-executor.download_s3_file:17|h
-SQS MessageID 0fa16960-befd-42c1-ae2c-f5be7aca89f8: Loop 2 - waiting 20s for task
-SQS MessageID 0fa16960-befd-42c1-ae2c-f5be7aca89f8: Task completed
-SQS MessageID 1c4534e0-202b-413d-bb07-f1fc12a3ae50: Loop 1 - waiting 20s for task
-""".splitlines()
-
-
-def test_receive_count_analyzer_executor() -> None:
-    input = TEST_INPUT_FROM_ANALYZER_EXECUTOR
-    receive_count = get_num_received_messages(input)
-    assert receive_count == 2
diff --git a/nomad/local/integration-tests.nomad b/nomad/local/python-integration-tests.nomad
similarity index 58%
rename from nomad/local/integration-tests.nomad
rename to nomad/local/python-integration-tests.nomad
index ab514d186d..7a4b276fc9 100644
--- a/nomad/local/integration-tests.nomad
+++ b/nomad/local/python-integration-tests.nomad
@@ -57,26 +57,6 @@ variable "grapl_root" {
   description = "Where to find the Grapl repo on the host OS (where Nomad runs)."
 }
 
-variable "plugin_work_queue_db" {
-  type = object({
-    hostname = string
-    port     = number
-    username = string
-    password = string
-  })
-  description = "Vars for plugin-work-queue database"
-}
-
-variable "organization_management_db" {
-  type = object({
-    hostname = string
-    port     = number
-    username = string
-    password = string
-  })
-  description = "Vars for organization-management database"
-}
-
 locals {
   log_level = "DEBUG"
 
@@ -86,7 +66,7 @@ locals {
   redis_port     = local._redis[1]
 }
 
-job "integration-tests" {
+job "python-integration-tests" {
   datacenters = ["dc1"]
   type        = "batch"
   parameterized {}
@@ -99,101 +79,6 @@ job "integration-tests" {
   # Specifies that this job is the most high priority job we have; nothing else should take precedence
   priority = 100
 
-  group "rust-integration-tests" {
-    restart {
-      # Make this a one-shot job
-      attempts = 0
-    }
-
-    network {
-      mode = "bridge"
-    }
-
-    # Enable service discovery
-    service {
-      name = "rust-integration-tests"
-      connect {
-        sidecar_service {
-          proxy {
-            upstreams {
-              # This is a hack, because IDK how to share locals across files
-              destination_name = "dgraph-alpha-0-grpc-public"
-              local_bind_port  = 9080
-            }
-
-            upstreams {
-              destination_name = "model-plugin-deployer"
-              # port unique but arbitrary - https://github.com/hashicorp/nomad/issues/7135
-              local_bind_port = 1000
-            }
-
-            upstreams {
-              destination_name = "plugin-work-queue"
-              # port unique but arbitrary - https://github.com/hashicorp/nomad/issues/7135
-              local_bind_port = 1001
-            }
-
-            upstreams {
-              destination_name = "organization-management"
-              # port unique but arbitrary - https://github.com/hashicorp/nomad/issues/7135
-              local_bind_port = 1002
-            }
-          }
-        }
-      }
-    }
-
-    task "rust-integration-tests" {
-      driver = "docker"
-
-      config {
-        image = var.container_images["rust-integration-tests"]
-      }
-
-      # This writes an env file that gets read by the task automatically
-      template {
-        data        = var.aws_env_vars_for_local
-        destination = "aws-env-vars-for-local.env"
-        env         = true
-      }
-
-      env {
-        AWS_REGION      = var.aws_region
-        GRAPL_LOG_LEVEL = local.log_level
-        # This is a hack, because IDK how to share locals across files
-        #MG_ALPHAS                   = local.alpha_grpc_connect_str # TODO: Figure out how to do this
-        MG_ALPHAS      = "localhost:9080"
-        RUST_BACKTRACE = 1
-        RUST_LOG       = local.log_level
-        REDIS_ENDPOINT = var.redis_endpoint
-
-        GRAPL_MODEL_PLUGIN_DEPLOYER_HOST = "0.0.0.0"
-        GRAPL_MODEL_PLUGIN_DEPLOYER_PORT = "${NOMAD_UPSTREAM_PORT_model-plugin-deployer}"
-
-        PLUGIN_WORK_QUEUE_BIND_ADDRESS = "0.0.0.0:${NOMAD_UPSTREAM_PORT_plugin-work-queue}"
-
-        PLUGIN_WORK_QUEUE_DB_HOSTNAME = var.plugin_work_queue_db.hostname
-        PLUGIN_WORK_QUEUE_DB_PORT     = var.plugin_work_queue_db.port
-        PLUGIN_WORK_QUEUE_DB_USERNAME = var.plugin_work_queue_db.username
-        PLUGIN_WORK_QUEUE_DB_PASSWORD = var.plugin_work_queue_db.password
-
-        ORGANIZATION_MANAGEMENT_ADDRESS      = "http://0.0.0.0:${NOMAD_UPSTREAM_PORT_organization_management}"
-        ORGANIZATION_MANAGEMENT_BIND_ADDRESS = "0.0.0.0:${NOMAD_UPSTREAM_PORT_organization_management}"
-
-        ORGANIZATION_MANAGEMENT_DB_HOSTNAME = var.organization_management_db.hostname
-        ORGANIZATION_MANAGEMENT_DB_PORT     = var.organization_management_db.port
-        ORGANIZATION_MANAGEMENT_DB_USERNAME = var.organization_management_db.username
-        ORGANIZATION_MANAGEMENT_DB_PASSWORD = var.organization_management_db.password
-
-        NOMAD_SERVICE_ADDRESS = "${attr.unique.network.ip-address}:4646"
-      }
-
-      resources {
-        memory = 1024
-      }
-    }
-  }
-
   group "python-integration-tests" {
     restart {
       # Make this a one-shot job. Absolute worst case, Buildkite reruns it for us.
diff --git a/nomad/integration-tests-new.nomad b/nomad/rust-integration-tests.nomad
similarity index 77%
rename from nomad/integration-tests-new.nomad
rename to nomad/rust-integration-tests.nomad
index 979ef17396..0527a70280 100644
--- a/nomad/integration-tests-new.nomad
+++ b/nomad/rust-integration-tests.nomad
@@ -31,11 +31,6 @@ variable "kafka_bootstrap_servers" {
   description = "The URL(s) (possibly comma-separated) of the Kafka bootstrap servers."
 }
 
-variable "pipeline_ingress_healthcheck_polling_interval_ms" {
-  type        = string
-  description = "The amount of time to wait between each healthcheck execution."
-}
-
 variable "integration_tests_kafka_consumer_group_name" {
   type        = string
   description = "The name of the consumer group the integration test consumers will join."
@@ -62,6 +57,16 @@ variable "dns_server" {
   default     = ""
 }
 
+variable "organization_management_db" {
+  type = object({
+    hostname = string
+    port     = number
+    username = string
+    password = string
+  })
+  description = "Vars for organization-management database"
+}
+
 variable "plugin_work_queue_db" {
   type = object({
     hostname = string
@@ -78,7 +83,7 @@ locals {
   dns_servers = [var.dns_server]
 }
 
-job "integration-tests-new" {
+job "rust-integration-tests" {
   datacenters = ["dc1"]
   type        = "batch"
   parameterized {}
@@ -91,7 +96,7 @@ job "integration-tests-new" {
   # Specifies that this job is the most high priority job we have; nothing else should take precedence
   priority = 100
 
-  group "integration-tests-new" {
+  group "rust-integration-tests" {
     restart {
       # Make this a one-shot job
       attempts = 0
@@ -106,7 +111,7 @@ job "integration-tests-new" {
 
     # Enable service discovery
     service {
-      name = "integration-tests-new"
+      name = "rust-integration-tests"
       connect {
         sidecar_service {
           proxy {
@@ -131,19 +136,29 @@ job "integration-tests-new" {
             }
 
             upstreams {
-              destination_name = "event-source"
+              destination_name = "organization-management"
               local_bind_port  = 1004
             }
+
+            upstreams {
+              destination_name = "model-plugin-deployer"
+              local_bind_port  = 1005
+            }
+
+            upstreams {
+              destination_name = "event-source"
+              local_bind_port  = 1006
+            }
           }
         }
       }
     }
 
-    task "rust-integration-tests-new" {
+    task "rust-integration-tests" {
       driver = "docker"
 
       config {
-        image = var.container_images["rust-integration-tests-new"]
+        image = var.container_images["rust-integration-tests"]
       }
 
       # This writes an env file that gets read by the task automatically
@@ -163,7 +178,17 @@ job "integration-tests-new" {
 
         KAFKA_BOOTSTRAP_SERVERS = var.kafka_bootstrap_servers
 
-        EVENT_SOURCE_CLIENT_ADDRESS      = "http://${NOMAD_UPSTREAM_ADDR_event-source}"
+        ORGANIZATION_MANAGEMENT_BIND_ADDRESS   = "0.0.0.0:1004" # not used but required due to clap
+        ORGANIZATION_MANAGEMENT_CLIENT_ADDRESS = "http://${NOMAD_UPSTREAM_ADDR_organization-management}"
+        ORGANIZATION_MANAGEMENT_DB_HOSTNAME    = var.organization_management_db.hostname
+        ORGANIZATION_MANAGEMENT_DB_PASSWORD    = var.organization_management_db.password
+        ORGANIZATION_MANAGEMENT_DB_PORT        = var.organization_management_db.port
+        ORGANIZATION_MANAGEMENT_DB_USERNAME    = var.organization_management_db.username
+
+        ORGANIZATION_MANAGEMENT_HEALTHCHECK_POLLING_INTERVAL_MS = 5000
+
+        EVENT_SOURCE_CLIENT_ADDRESS = "http://${NOMAD_UPSTREAM_ADDR_event-source}"
+
         PIPELINE_INGRESS_CLIENT_ADDRESS  = "http://${NOMAD_UPSTREAM_ADDR_pipeline-ingress}"
         PLUGIN_REGISTRY_CLIENT_ADDRESS   = "http://0.0.0.0:${NOMAD_UPSTREAM_PORT_plugin-registry}"
         PLUGIN_WORK_QUEUE_CLIENT_ADDRESS = "http://${NOMAD_UPSTREAM_ADDR_plugin-work-queue}"
@@ -174,6 +199,9 @@ job "integration-tests-new" {
         # (this is an invalid topic name, so it'd throw an error if consumed)
         KAFKA_CONSUMER_TOPIC = "<replace me at integration test setup>"
 
+        GRAPL_MODEL_PLUGIN_DEPLOYER_HOST = "0.0.0.0"
+        GRAPL_MODEL_PLUGIN_DEPLOYER_PORT = "${NOMAD_UPSTREAM_PORT_model-plugin-deployer}"
+
         NOMAD_SERVICE_ADDRESS = "${attr.unique.network.ip-address}:4646"
 
         PLUGIN_WORK_QUEUE_DB_HOSTNAME = var.plugin_work_queue_db.hostname
diff --git a/pulumi/grapl/Pulumi.testing.yaml b/pulumi/grapl/Pulumi.testing.yaml
index d35ed1018f..fa6cc57d6b 100644
--- a/pulumi/grapl/Pulumi.testing.yaml
+++ b/pulumi/grapl/Pulumi.testing.yaml
@@ -3,11 +3,6 @@ config:
   grapl:GRAPL_OPERATIONAL_ALARMS_EMAIL: operational-alarms@graplsecurity.com
   grapl:cloudsmith-repository-name: grapl/testing
   grapl:env_vars:
-    analyzer-dispatcher:
-      RUST_BACKTRACE: "1"
-      RUST_LOG: DEBUG
-    analyzer-executor:
-      GRAPL_LOG_LEVEL: DEBUG
     engagement-creator:
       GRAPL_LOG_LEVEL: DEBUG
     graph-merger:
diff --git a/pulumi/integration_tests/BUILD b/pulumi/integration_tests/BUILD
deleted file mode 100644
index db46e8d6c9..0000000000
--- a/pulumi/integration_tests/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources()
diff --git a/pulumi/integration_tests/Pulumi.testing.yaml b/pulumi/integration_tests/Pulumi.testing.yaml
deleted file mode 100644
index 5947534f18..0000000000
--- a/pulumi/integration_tests/Pulumi.testing.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-config:
-  aws:region: us-east-1
-  integration-tests:cloudsmith-repository-name: grapl/testing
-  integration-tests:confluent-environment-name: "testing"
diff --git a/pulumi/integration_tests/Pulumi.yaml b/pulumi/integration_tests/Pulumi.yaml
deleted file mode 100644
index 0d01f7c57e..0000000000
--- a/pulumi/integration_tests/Pulumi.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-name: integration-tests
-runtime: python
-description: Deploy integration tests to Nomad
diff --git a/pulumi/python_integration_tests/BUILD b/pulumi/python_integration_tests/BUILD
new file mode 100644
index 0000000000..f9c78d1b49
--- /dev/null
+++ b/pulumi/python_integration_tests/BUILD
@@ -0,0 +1,6 @@
+python_sources()
+
+pex_binary(
+    name="__main__",
+    entry_point="__main__.py",
+)
diff --git a/pulumi/python_integration_tests/Pulumi.local-grapl-python-integration-tests.yaml b/pulumi/python_integration_tests/Pulumi.local-grapl-python-integration-tests.yaml
new file mode 100644
index 0000000000..2f299c2bed
--- /dev/null
+++ b/pulumi/python_integration_tests/Pulumi.local-grapl-python-integration-tests.yaml
@@ -0,0 +1,10 @@
+encryptionsalt: v1:KRH5XXw/Vv4=:v1:Mi1EOttYFIuFy8Zl:u/QoTfZuh9c5m6Jyw2L3mAKC+VNs8g==
+config:
+  aws:accessKey: test
+  aws:region: us-east-1
+  aws:secretKey: test
+  aws:skipCredentialsValidation: "true"
+  aws:skipRequestingAccountId: "true"
+  # Host OS's Nomad instance
+  nomad:address: http://host.docker.internal:4646
+  python-integration-tests:confluent-environment-name: "local-grapl"
diff --git a/pulumi/python_integration_tests/Pulumi.yaml b/pulumi/python_integration_tests/Pulumi.yaml
new file mode 100644
index 0000000000..229514d33d
--- /dev/null
+++ b/pulumi/python_integration_tests/Pulumi.yaml
@@ -0,0 +1,3 @@
+name: python-integration-tests
+runtime: python
+description: Deploy python integration tests to Nomad
diff --git a/pulumi/integration_tests/__init__.py b/pulumi/python_integration_tests/__init__.py
similarity index 100%
rename from pulumi/integration_tests/__init__.py
rename to pulumi/python_integration_tests/__init__.py
diff --git a/pulumi/python_integration_tests/__main__.py b/pulumi/python_integration_tests/__main__.py
new file mode 100644
index 0000000000..9d33950d47
--- /dev/null
+++ b/pulumi/python_integration_tests/__main__.py
@@ -0,0 +1,125 @@
+import sys
+
+sys.path.insert(0, "..")
+
+import os
+from typing import Mapping, Optional, cast
+
+import pulumi_aws as aws
+from infra import config
+from infra.artifacts import ArtifactGetter
+from infra.autotag import register_auto_tags
+from infra.docker_images import DockerImageId, DockerImageIdBuilder
+from infra.hashicorp_provider import get_nomad_provider_address
+from infra.kafka import Kafka
+from infra.nomad_job import NomadJob, NomadVars
+from infra.nomad_service_postgres import NomadServicePostgresDbArgs
+from infra.path import path_from_root
+
+import pulumi
+
+
+def _python_integration_container_images(
+    artifacts: ArtifactGetter,
+) -> Mapping[str, DockerImageId]:
+    """
+    Build a map of {task name -> docker image identifier}.
+    """
+    builder = DockerImageIdBuilder(
+        container_repository=config.container_repository(),
+        artifacts=artifacts,
+    )
+
+    return {
+        "python-integration-tests": builder.build_with_tag("python-integration-tests"),
+    }
+
+
+def main() -> None:
+    ##### Preamble
+    stack_name = config.STACK_NAME
+
+    pulumi_config = pulumi.Config()
+    artifacts = ArtifactGetter.from_config(pulumi_config)
+
+    # These tags will be added to all provisioned infrastructure
+    # objects.
+    register_auto_tags(
+        {"pulumi:project": pulumi.get_project(), "pulumi:stack": stack_name}
+    )
+
+    nomad_provider: Optional[pulumi.ProviderResource] = None
+    if not config.LOCAL_GRAPL:
+        nomad_server_stack = pulumi.StackReference(f"grapl/nomad/{stack_name}")
+        nomad_provider = get_nomad_provider_address(nomad_server_stack)
+
+    ##### Business Logic
+    grapl_stack = GraplStack(stack_name)
+
+    kafka = Kafka(
+        "kafka",
+        confluent_environment_name=pulumi_config.require("confluent-environment-name"),
+        create_local_topics=False,
+    )
+
+    integration_tests_kafka_credentials = kafka.service_credentials("integration-tests")
+
+    if config.LOCAL_GRAPL:
+        # We don't do Python integration tests in AWS yet, mostly because the
+        # current Python Pants integration test setup is funky and requires an
+        # on-disk Grapl repo.
+        # FIXME: make python integration tests work in AWS.
+
+        python_integration_test_job_vars: NomadVars = {
+            "aws_env_vars_for_local": grapl_stack.aws_env_vars_for_local,
+            "aws_region": aws.get_region().name,
+            "container_images": _python_integration_container_images(artifacts),
+            "docker_user": os.environ["DOCKER_USER"],
+            "grapl_root": os.environ["GRAPL_ROOT"],
+            "redis_endpoint": grapl_stack.redis_endpoint,
+            "schema_properties_table_name": grapl_stack.schema_properties_table_name,
+            "test_user_name": grapl_stack.test_user_name,
+            "test_user_password_secret_id": grapl_stack.test_user_password_secret_id,
+        }
+
+        python_integration_tests = NomadJob(
+            "python-integration-tests",
+            jobspec=path_from_root(
+                "nomad/local/python-integration-tests.nomad"
+            ).resolve(),
+            vars=python_integration_test_job_vars,
+            opts=pulumi.ResourceOptions(provider=nomad_provider),
+        )
+
+
+class GraplStack:
+    def __init__(self, stack_name: str) -> None:
+        self.upstream_stack_name = (
+            "local-grapl" if config.LOCAL_GRAPL else f"grapl/grapl/{stack_name}"
+        )
+        ref = pulumi.StackReference(self.upstream_stack_name)
+
+        def require_str(key: str) -> str:
+            return cast(str, ref.require_output(key))
+
+        self.aws_env_vars_for_local = require_str("aws-env-vars-for-local")
+        self.analyzer_bucket = require_str("analyzers-bucket")
+        self.redis_endpoint = require_str("redis-endpoint")
+        self.schema_properties_table_name = require_str("schema-properties-table")
+        self.schema_table_name = require_str("schema-table")
+        self.sysmon_log_bucket = require_str("sysmon-log-bucket")
+        self.test_user_name = require_str("test-user-name")
+
+        self.plugin_work_queue_db = cast(
+            NomadServicePostgresDbArgs, ref.require_output("plugin-work-queue-db")
+        )
+
+        self.organization_management_db = cast(
+            NomadServicePostgresDbArgs, ref.require_output("organization-management-db")
+        )
+
+        self.test_user_password_secret_id = require_str("test-user-password-secret-id")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/pulumi/rust_integration_tests/BUILD b/pulumi/rust_integration_tests/BUILD
new file mode 100644
index 0000000000..f9c78d1b49
--- /dev/null
+++ b/pulumi/rust_integration_tests/BUILD
@@ -0,0 +1,6 @@
+python_sources()
+
+pex_binary(
+    name="__main__",
+    entry_point="__main__.py",
+)
diff --git a/pulumi/integration_tests/Pulumi.local-grapl-integration-tests.yaml b/pulumi/rust_integration_tests/Pulumi.local-grapl-rust-integration-tests.yaml
similarity index 82%
rename from pulumi/integration_tests/Pulumi.local-grapl-integration-tests.yaml
rename to pulumi/rust_integration_tests/Pulumi.local-grapl-rust-integration-tests.yaml
index b68dde1b74..db1cb3a8ed 100644
--- a/pulumi/integration_tests/Pulumi.local-grapl-integration-tests.yaml
+++ b/pulumi/rust_integration_tests/Pulumi.local-grapl-rust-integration-tests.yaml
@@ -7,4 +7,4 @@ config:
   aws:skipRequestingAccountId: "true"
   # Host OS's Nomad instance
   nomad:address: http://host.docker.internal:4646
-  integration-tests:confluent-environment-name: "local-grapl"
+  rust-integration-tests:confluent-environment-name: "local-grapl"
diff --git a/pulumi/rust_integration_tests/Pulumi.testing.yaml b/pulumi/rust_integration_tests/Pulumi.testing.yaml
new file mode 100644
index 0000000000..0085158142
--- /dev/null
+++ b/pulumi/rust_integration_tests/Pulumi.testing.yaml
@@ -0,0 +1,4 @@
+config:
+  aws:region: us-east-1
+  rust-integration-tests:cloudsmith-repository-name: grapl/testing
+  rust-integration-tests:confluent-environment-name: "testing"
diff --git a/pulumi/rust_integration_tests/Pulumi.yaml b/pulumi/rust_integration_tests/Pulumi.yaml
new file mode 100644
index 0000000000..4d5fb93e12
--- /dev/null
+++ b/pulumi/rust_integration_tests/Pulumi.yaml
@@ -0,0 +1,3 @@
+name: rust-integration-tests
+runtime: python
+description: Deploy rust integration tests to Nomad
diff --git a/pulumi/integration_tests/README.md b/pulumi/rust_integration_tests/README.md
similarity index 100%
rename from pulumi/integration_tests/README.md
rename to pulumi/rust_integration_tests/README.md
diff --git a/src/python/analyzer_executor/src/analyzer_executor_lib/__init__.py b/pulumi/rust_integration_tests/__init__.py
similarity index 100%
rename from src/python/analyzer_executor/src/analyzer_executor_lib/__init__.py
rename to pulumi/rust_integration_tests/__init__.py
diff --git a/pulumi/integration_tests/__main__.py b/pulumi/rust_integration_tests/__main__.py
similarity index 61%
rename from pulumi/integration_tests/__main__.py
rename to pulumi/rust_integration_tests/__main__.py
index da1bf6bd25..d46538aeb3 100644
--- a/pulumi/integration_tests/__main__.py
+++ b/pulumi/rust_integration_tests/__main__.py
@@ -2,7 +2,6 @@
 
 sys.path.insert(0, "..")
 
-import os
 from typing import Mapping, Optional, cast
 
 import pulumi_aws as aws
@@ -19,7 +18,7 @@
 import pulumi
 
 
-def _integration_container_images(
+def _rust_integration_container_images(
     artifacts: ArtifactGetter,
 ) -> Mapping[str, DockerImageId]:
     """
@@ -31,29 +30,10 @@ def _integration_container_images(
     )
 
     return {
-        "python-integration-tests": builder.build_with_tag("python-integration-tests"),
         "rust-integration-tests": builder.build_with_tag("rust-integration-tests"),
     }
 
 
-def _integration_new_container_images(
-    artifacts: ArtifactGetter,
-) -> Mapping[str, DockerImageId]:
-    """
-    Build a map of {task name -> docker image identifier}.
-    """
-    builder = DockerImageIdBuilder(
-        container_repository=config.container_repository(),
-        artifacts=artifacts,
-    )
-
-    return {
-        "rust-integration-tests-new": builder.build_with_tag(
-            "rust-integration-tests-new"
-        )
-    }
-
-
 def main() -> None:
     ##### Preamble
     stack_name = config.STACK_NAME
@@ -83,10 +63,10 @@ def main() -> None:
 
     integration_tests_kafka_credentials = kafka.service_credentials("integration-tests")
 
-    integration_tests_new_job_vars: NomadVars = {
+    rust_integration_tests_job_vars: NomadVars = {
         "aws_env_vars_for_local": grapl_stack.aws_env_vars_for_local,
         "aws_region": aws.get_region().name,
-        "container_images": _integration_new_container_images(artifacts),
+        "container_images": _rust_integration_container_images(artifacts),
         "dns_server": config.CONSUL_DNS_IP,
         "integration_tests_kafka_consumer_group_name": kafka.consumer_group(
             "integration-tests"
@@ -99,43 +79,17 @@ def main() -> None:
         ),
         "kafka_bootstrap_servers": kafka.bootstrap_servers(),
         "rust_log": log_levels.RUST_LOG_LEVELS,
-        "pipeline_ingress_healthcheck_polling_interval_ms": grapl_stack.pipeline_ingress_healthcheck_polling_interval_ms,
+        "organization_management_db": grapl_stack.organization_management_db,
         "plugin_work_queue_db": grapl_stack.plugin_work_queue_db,
     }
 
-    integration_tests_new = NomadJob(
-        "integration-tests-new",
-        jobspec=path_from_root("nomad/integration-tests-new.nomad").resolve(),
-        vars=integration_tests_new_job_vars,
+    rust_integration_tests = NomadJob(
+        "rust-integration-tests",
+        jobspec=path_from_root("nomad/rust-integration-tests.nomad").resolve(),
+        vars=rust_integration_tests_job_vars,
         opts=pulumi.ResourceOptions(provider=nomad_provider),
     )
 
-    if config.LOCAL_GRAPL:
-        # We don't do integration tests in AWS yet, mostly because the current
-        # Python Pants integration test setup is funky and requires an on-disk
-        # Grapl repo.
-
-        integration_test_job_vars: NomadVars = {
-            "aws_env_vars_for_local": grapl_stack.aws_env_vars_for_local,
-            "aws_region": aws.get_region().name,
-            "container_images": _integration_container_images(artifacts),
-            "docker_user": os.environ["DOCKER_USER"],
-            "grapl_root": os.environ["GRAPL_ROOT"],
-            "redis_endpoint": grapl_stack.redis_endpoint,
-            "schema_properties_table_name": grapl_stack.schema_properties_table_name,
-            "test_user_name": grapl_stack.test_user_name,
-            "test_user_password_secret_id": grapl_stack.test_user_password_secret_id,
-            "plugin_work_queue_db": grapl_stack.plugin_work_queue_db,
-            "organization_management_db": grapl_stack.organization_management_db,
-        }
-
-        integration_tests = NomadJob(
-            "integration-tests",
-            jobspec=path_from_root("nomad/local/integration-tests.nomad").resolve(),
-            vars=integration_test_job_vars,
-            opts=pulumi.ResourceOptions(provider=nomad_provider),
-        )
-
 
 class GraplStack:
     def __init__(self, stack_name: str) -> None:
@@ -147,6 +101,7 @@ def __init__(self, stack_name: str) -> None:
         def require_str(key: str) -> str:
             return cast(str, ref.require_output(key))
 
+        # FIXME: audit these, they're not all required for rust integration tests
         self.aws_env_vars_for_local = require_str("aws-env-vars-for-local")
         self.analyzer_bucket = require_str("analyzers-bucket")
         self.redis_endpoint = require_str("redis-endpoint")
@@ -158,14 +113,11 @@ def require_str(key: str) -> str:
         self.plugin_work_queue_db = cast(
             NomadServicePostgresDbArgs, ref.require_output("plugin-work-queue-db")
         )
+
         self.organization_management_db = cast(
             NomadServicePostgresDbArgs, ref.require_output("organization-management-db")
         )
 
-        self.pipeline_ingress_healthcheck_polling_interval_ms = require_str(
-            "pipeline-ingress-healthcheck-polling-interval-ms"
-        )
-
         self.test_user_password_secret_id = require_str("test-user-password-secret-id")
 
 
diff --git a/src/python/analyzer_executor/pytest.ini b/src/python/analyzer_executor/pytest.ini
deleted file mode 100644
index 223ea5fb73..0000000000
--- a/src/python/analyzer_executor/pytest.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[pytest]
-markers =
-    integration_test: marks test as integration_test (deselect with '-m "not integration_test"')
diff --git a/src/python/analyzer_executor/tests/BUILD b/src/python/analyzer_executor/tests/BUILD
deleted file mode 100644
index 3ac0f07da0..0000000000
--- a/src/python/analyzer_executor/tests/BUILD
+++ /dev/null
@@ -1,13 +0,0 @@
-python_tests(
-    extra_env_vars=[
-        "HITCACHE_ADDR",
-        "HITCACHE_PORT",
-        "MESSAGECACHE_ADDR",
-        "MESSAGECACHE_PORT",
-        "GRAPL_ANALYZERS_BUCKET",
-        "GRAPL_ANALYZER_MATCHED_SUBGRAPHS_BUCKET",
-        "GRAPL_LOG_LEVEL",
-        "GRAPL_MODEL_PLUGINS_BUCKET",
-        "IS_RETRY",
-    ]
-)
diff --git a/src/python/analyzer_executor/tests/test_cache_integration.py b/src/python/analyzer_executor/tests/test_cache_integration.py
deleted file mode 100644
index 2604884f59..0000000000
--- a/src/python/analyzer_executor/tests/test_cache_integration.py
+++ /dev/null
@@ -1,123 +0,0 @@
-import os
-from copy import deepcopy
-from typing import Mapping, Optional
-
-import hypothesis
-import pytest
-from analyzer_executor_lib.analyzer_executor import AnalyzerExecutor
-from hypothesis import strategies as st
-
-SAMPLE_ADDR = "localhost"
-SAMPLE_PORT = "12345"
-
-NonemptyStringStrategy = st.text(min_size=3, max_size=64)
-
-
-class AnalyzerExecutorCacheDeleters:
-    def __init__(self, analyzer_executor: AnalyzerExecutor) -> None:
-        self.analyzer_executor = analyzer_executor
-
-    def delete_msg_cache(self, file: str, node_key: str, msg_id: str) -> None:
-        event_hash = self.analyzer_executor.to_event_hash((file, node_key, msg_id))
-        self.analyzer_executor.message_cache.delete(event_hash)
-
-    def delete_hit_cache(self, file: str, node_key: str) -> None:
-        event_hash = self.analyzer_executor.to_event_hash((file, node_key))
-        self.analyzer_executor.hit_cache.delete(event_hash)
-
-
-def fake_os_env(
-    env_addr: Optional[str] = None,
-    env_port: Optional[str] = None,
-) -> Mapping[str, str]:
-    new_os_environ = deepcopy(os.environ)
-    if env_addr:
-        new_os_environ["MESSAGECACHE_ADDR"] = env_addr
-        new_os_environ["HITCACHE_ADDR"] = env_addr
-    else:
-        del new_os_environ["MESSAGECACHE_ADDR"]
-        del new_os_environ["HITCACHE_ADDR"]
-
-    if env_port:
-        new_os_environ["MESSAGECACHE_PORT"] = env_port
-        new_os_environ["HITCACHE_PORT"] = env_port
-    else:
-        del new_os_environ["MESSAGECACHE_PORT"]
-        del new_os_environ["HITCACHE_PORT"]
-    return new_os_environ
-
-
-@pytest.mark.integration_test
-def test_connection_info() -> None:
-    """
-    Ensures exceptions are raised for incomplete connection info.
-    """
-
-    with pytest.raises(ValueError):
-        ae = AnalyzerExecutor.from_env(fake_os_env(env_addr=SAMPLE_ADDR, env_port=None))
-
-    with pytest.raises(ValueError):
-        ae = AnalyzerExecutor.from_env(fake_os_env(env_addr=None, env_port=SAMPLE_PORT))
-
-    with pytest.raises(ValueError):
-        ae = AnalyzerExecutor.from_env(fake_os_env(env_addr=None, env_port=None))
-
-
-@hypothesis.given(
-    k1=NonemptyStringStrategy,
-    k2=NonemptyStringStrategy,
-)
-@hypothesis.settings(
-    # Doesn't like the Pytest fixture mixed with Hypothesis givens.
-    # It's okay, since the fixture just returns a function.
-    suppress_health_check=[hypothesis.HealthCheck.function_scoped_fixture],
-    deadline=None,
-)
-@pytest.mark.integration_test
-def test_hit_cache(
-    k1: str,
-    k2: str,
-) -> None:
-    """
-    Initializes the AnalyzerExecutor singleton with Redis connection params
-    sourced from the environment, expecting hit cache to populate.
-    """
-    ae = AnalyzerExecutor.from_env()
-
-    assert not ae.check_hit_cache(k1, k2)
-    ae.update_hit_cache(k1, k2)
-    assert ae.check_hit_cache(k1, k2)
-    # Clean up, because Hypothesis provides duplicate inputs
-    AnalyzerExecutorCacheDeleters(ae).delete_hit_cache(k1, k2)
-    assert not ae.check_hit_cache(k1, k2)
-
-
-@hypothesis.given(
-    k1=NonemptyStringStrategy,
-    k2=NonemptyStringStrategy,
-    k3=NonemptyStringStrategy,
-)
-@hypothesis.settings(
-    # Doesn't like the Pytest fixture mixed with Hypothesis givens.
-    # It's okay, since the fixture just returns a function.
-    suppress_health_check=[hypothesis.HealthCheck.function_scoped_fixture],
-    deadline=None,
-)
-@pytest.mark.integration_test
-def test_message_cache(
-    k1: str,
-    k2: str,
-    k3: str,
-) -> None:
-    """
-    Initializes the AnalyzerExecutor singleton with Redis connection params
-    sourced from the environment, expecting message cache to populate.
-    """
-    ae = AnalyzerExecutor.from_env()
-
-    assert not ae.check_msg_cache(k1, k2, k3)
-    ae.update_msg_cache(k1, k2, k3)
-    assert ae.check_msg_cache(k1, k2, k3)
-    # Clean up, because Hypothesis provides duplicate inputs
-    AnalyzerExecutorCacheDeleters(ae).delete_msg_cache(k1, k2, k3)
-    assert not ae.check_msg_cache(k1, k2, k3)
diff --git a/src/python/analyzer_executor/tests/test_sqs_timeout_manager.py b/src/python/analyzer_executor/tests/test_sqs_timeout_manager.py
deleted file mode 100644
index 84387bd8bd..0000000000
--- a/src/python/analyzer_executor/tests/test_sqs_timeout_manager.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from __future__ import annotations
-
-import asyncio
-import unittest
-from unittest.mock import Mock
-
-import pytest
-from grapl_common.sqs.sqs_timeout_manager import (
-    SqsTimeoutManager,
-    SqsTimeoutManagerException,
-)
-from grapl_common.sqs.sqs_types import SQSMessageId, SQSReceiptHandle
-
-
-class TestSqsTimeoutManager(unittest.TestCase):
-    def test_keep_alive_fails(self) -> None:
-        f = SqsTimeoutManagerFixture()
-        with pytest.raises(SqsTimeoutManagerException):
-            asyncio.run(f.timeout_manager.keep_alive())
-
-        assert f.state == "started"
-        assert f.sqs_client.change_message_visibility.call_count == 2
-
-    def test_keep_alive_completion(self) -> None:
-        f = SqsTimeoutManagerFixture()
-        f.timeout_manager.visibility_timeout = 20
-
-        asyncio.run(f.timeout_manager.keep_alive())
-        assert f.state == "completed"
-        assert f.sqs_client.change_message_visibility.call_count == 1
-
-    def test_sleep_and_timeout(self) -> None:
-        f = SqsTimeoutManagerFixture()
-        f.timeout_manager.visibility_timeout = 30
-
-        # initial visibility is 30. then, 10 seconds before that:
-        assert f.timeout_manager._get_next_sleep(1) == 20
-        # we promote the visibility to 60.
-        assert f.timeout_manager._get_next_visibility(1) == 60
-
-        # okay, so visibility is 60, and SQS treats it as if 0 seconds have elapsed.
-        # Let's sleep 60-10 seconds...
-        assert f.timeout_manager._get_next_sleep(2) == 50
-        # at which point, we promote again, to 90
-        assert f.timeout_manager._get_next_visibility(2) == 90
-
-        _quiet_never_awaited_error(f)
-
-
-def _quiet_never_awaited_error(f: SqsTimeoutManagerFixture) -> None:
-    """
-    Some tests that consume SqsTimeoutManagerFixture never await. That's OK.
-    Solves "coroutine was never awaited"
-    """
-
-    async def cancel_it() -> None:
-        asyncio.create_task(f.timeout_manager.coroutine).cancel()
-
-    asyncio.run(cancel_it())
-
-
-class SqsTimeoutManagerFixture:
-    def __init__(self) -> None:
-        # 22 seconds is not enough for
-        self.state = "initialized"
-
-        async def wait_25_secs() -> None:
-            self.state = "started"
-            await asyncio.sleep(25)
-            self.state = "completed"
-
-        self.sqs_client = Mock()
-
-        self.timeout_manager = SqsTimeoutManager(
-            sqs_client=self.sqs_client,
-            queue_url="queue_url",
-            receipt_handle=SQSReceiptHandle("receipt_handle"),
-            message_id=SQSMessageId("message_id"),
-            coroutine=wait_25_secs(),
-            visibility_timeout=11,
-            num_loops=2,
-        )
diff --git a/src/python/grapl-tests-common/grapl_tests_common/upload_logs.py b/src/python/grapl-tests-common/grapl_tests_common/upload_logs.py
deleted file mode 100755
index 64cfe72f9e..0000000000
--- a/src/python/grapl-tests-common/grapl_tests_common/upload_logs.py
+++ /dev/null
@@ -1,184 +0,0 @@
-from __future__ import annotations
-
-import json
-import os
-import random
-import string
-import time
-import uuid
-from dataclasses import dataclass
-from datetime import datetime
-from os import PathLike
-from typing import TYPE_CHECKING, Optional, cast
-
-from grapl_common.env_helpers import S3ClientFactory, SQSClientFactory
-from python_proto.pipeline import Metadata, OldEnvelope
-
-if TYPE_CHECKING:
-    from mypy_boto3_s3 import S3Client
-    from mypy_boto3_sqs import SQSClient
-
-import boto3
-import zstd  # type: ignore
-
-
-def rand_str(l: int) -> str:
-    return "".join(
-        random.choice(string.ascii_uppercase + string.digits) for _ in range(l)
-    )
-
-
-def into_sqs_message(bucket: str, key: str) -> str:
-    return json.dumps(
-        {
-            "Records": [
-                {
-                    "awsRegion": "us-east-1",
-                    "eventTime": datetime.utcnow().isoformat(),
-                    "principalId": {
-                        "principalId": None,
-                    },
-                    "requestParameters": {
-                        "sourceIpAddress": None,
-                    },
-                    "responseElements": {},
-                    "s3": {
-                        "schemaVersion": None,
-                        "configurationId": None,
-                        "bucket": {
-                            "name": bucket,
-                            "ownerIdentity": {
-                                "principalId": None,
-                            },
-                        },
-                        "object": {
-                            "key": key,
-                            "size": 0,
-                            "urlDecodedKey": None,
-                            "versionId": None,
-                            "eTag": None,
-                            "sequencer": None,
-                        },
-                    },
-                }
-            ]
-        }
-    )
-
-
-@dataclass
-class GeneratorOptions:
-    bucket: str
-    queue_url: str
-    key_infix: str
-
-
-class SysmonGeneratorOptions(GeneratorOptions):
-    def __init__(self, bucket: str, queue_url: str) -> None:
-        super().__init__(
-            queue_url=queue_url,
-            bucket=bucket,
-            key_infix="sysmon",
-        )
-
-
-class OSQueryGeneratorOptions(GeneratorOptions):
-    def __init__(self, bucket: str, queue_url: str) -> None:
-        super().__init__(
-            queue_url=queue_url,
-            bucket=bucket,
-            key_infix="osquery",
-        )
-
-
-def upload_logs(
-    deployment_name: str,
-    logfile: PathLike,
-    generator_options: GeneratorOptions,
-    s3_client: Optional[S3Client] = None,
-    sqs_client: Optional[SQSClient] = None,
-) -> None:
-    print(f"Writing events from {logfile} to {deployment_name}")
-
-    requires_manual_eventing = deployment_name == "local-grapl"
-
-    s3 = s3_client or S3ClientFactory(boto3).from_env()
-    sqs = sqs_client or SQSClientFactory(boto3).from_env()
-
-    f = open(logfile, "rb")
-    compressed_log_data = cast(bytes, zstd.compress(f.read()))
-    original_filename = os.path.basename(f.name)
-    compressed_file_name = original_filename + ".zstd"
-
-    bucket = generator_options.bucket
-    queue_url = generator_options.queue_url
-
-    epoch = int(time.time())
-
-    key = (
-        str(epoch - (epoch % (24 * 60 * 60)))
-        + f"/{generator_options.key_infix}/"
-        + compressed_file_name
-    )
-
-    envelope = OldEnvelope(
-        metadata=Metadata(
-            tenant_id=uuid.uuid4(),  # FIXME: be smarter here.
-            trace_id=uuid.uuid4(),  # FIXME: and here.
-            event_source_id=uuid.uuid4(),  # FIXME: and here.
-            created_time=datetime.utcnow(),
-            last_updated_time=datetime.utcnow(),
-        ),
-        inner_message=compressed_log_data,
-        inner_type="(╯°□°)╯︵ ┻━┻",
-    )
-
-    s3.put_object(Body=envelope.serialize(), Bucket=bucket, Key=key)
-
-    # local-grapl relies on manual eventing
-    if requires_manual_eventing:
-        sqs.send_message(
-            QueueUrl=queue_url,
-            MessageBody=into_sqs_message(bucket=bucket, key=key),
-        )
-
-    print(f"Completed uploading {logfile} at {time.ctime()}")
-
-
-def upload_sysmon_logs(
-    deployment_name: str,
-    logfile: PathLike,
-    log_bucket: str,
-    queue_url: str,
-    s3_client: Optional[S3Client] = None,
-    sqs_client: Optional[SQSClient] = None,
-) -> None:
-
-    upload_logs(
-        deployment_name=deployment_name,
-        logfile=logfile,
-        generator_options=SysmonGeneratorOptions(
-            bucket=log_bucket, queue_url=queue_url
-        ),
-        s3_client=s3_client,
-        sqs_client=sqs_client,
-    )
-
-
-def upload_osquery_logs(
-    deployment_name: str,
-    logfile: PathLike,
-    log_bucket: str,
-    queue_url: str,
-    s3_client: Optional[S3Client] = None,
-    sqs_client: Optional[SQSClient] = None,
-) -> None:
-    upload_logs(
-        deployment_name=deployment_name,
-        logfile=logfile,
-        generator_options=OSQueryGeneratorOptions(
-            bucket=log_bucket, queue_url=queue_url
-        ),
-        s3_client=s3_client,
-        sqs_client=sqs_client,
-    )
diff --git a/src/python/graplctl/tests/fake_uploads/BUILD b/src/python/graplctl/tests/fake_uploads/BUILD
deleted file mode 100644
index 708e4edda0..0000000000
--- a/src/python/graplctl/tests/fake_uploads/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources(sources=[])
diff --git a/src/python/graplctl/tests/fake_uploads/__init__.py b/src/python/graplctl/tests/fake_uploads/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/python/graplctl/tests/fake_uploads/fake_analyzer/BUILD b/src/python/graplctl/tests/fake_uploads/fake_analyzer/BUILD
deleted file mode 100644
index db46e8d6c9..0000000000
--- a/src/python/graplctl/tests/fake_uploads/fake_analyzer/BUILD
+++ /dev/null
@@ -1,1 +0,0 @@
-python_sources()
diff --git a/src/python/graplctl/tests/fake_uploads/fake_analyzer/__init__.py b/src/python/graplctl/tests/fake_uploads/fake_analyzer/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/python/graplctl/tests/fake_uploads/fake_analyzer/main.py b/src/python/graplctl/tests/fake_uploads/fake_analyzer/main.py
deleted file mode 100644
index 492b4c2ed7..0000000000
--- a/src/python/graplctl/tests/fake_uploads/fake_analyzer/main.py
+++ /dev/null
@@ -1,1 +0,0 @@
-# Hey, I'm a fake analyzer.
diff --git a/src/python/graplctl/tests/test_upload.py b/src/python/graplctl/tests/test_upload.py
deleted file mode 100644
index 1e4e8da5d8..0000000000
--- a/src/python/graplctl/tests/test_upload.py
+++ /dev/null
@@ -1,138 +0,0 @@
-from unittest.mock import ANY, MagicMock, call
-
-import pytest
-from tests.fake_uploads.fake_analyzer import main as fake_analyzer_main_py
-from tests.shared import BotoSessionMock, invoke_with_default_args, patch_boto3_session
-
-
-def test_upload__not_provisioned_yet() -> None:
-    with patch_boto3_session() as mock_session:
-        _mock_grapl_is_provisioned(mock_session, is_provisioned=False)
-        with pytest.raises(AssertionError) as raised:
-            result = invoke_with_default_args(
-                [
-                    "upload",
-                    "analyzer",
-                ],
-            )
-
-    assert "You can't upload anything to grapl until it's provisioned" in str(
-        raised.value
-    )
-
-
-def test_upload_analyzer__path_is_wrong() -> None:
-    main_py_path = "some_inexistent_dir/main.py"
-    with patch_boto3_session() as mock_session:
-        result = invoke_with_default_args(
-            [
-                "upload",
-                "analyzer",
-                "--analyzer_main_py",
-                main_py_path,
-            ],
-        )
-    assert result.exit_code != 0
-    assert f"File '{main_py_path}' does not exist" in result.output
-
-
-def test_upload_analyzer__calls_s3() -> None:
-    main_py_path = fake_analyzer_main_py.__file__
-    with patch_boto3_session() as mock_session:
-        result = invoke_with_default_args(
-            [
-                "upload",
-                "analyzer",
-                "--analyzer_main_py",
-                str(main_py_path),
-                "--analyzers-bucket",
-                "fake-deployment-analyzers-bucket",
-            ],
-        )
-
-    mock_s3_client = mock_session.client("s3")
-    mock_s3_client.put_object.assert_called_with(
-        Body=ANY,  # technically, you could read the contents of `fake_analyzer_main` but whatever
-        Bucket="fake-deployment-analyzers-bucket",
-        Key="analyzers/fake_analyzer/main.py",
-    )
-    assert result.exit_code == 0
-    assert result.output == "Uploaded analyzer 'fake_analyzer'\n"
-
-
-def test_upload_sysmon__calls_s3() -> None:
-    with patch_boto3_session() as mock_session:
-        sample_data_path = "etc/sample_data/eventlog.xml"
-        result = invoke_with_default_args(
-            [
-                "upload",
-                "sysmon",
-                "--logfile",
-                sample_data_path,
-                "--log-bucket",
-                "fake-deployment-sysmon-log-bucket",
-                "--queue-url",
-                "fake-sysmon-queue",
-            ],
-        )
-
-    mock_s3_client = mock_session.client("s3")
-    mock_s3_client.put_object.assert_has_calls(
-        [
-            call(
-                Body=ANY,
-                Bucket="fake-deployment-sysmon-log-bucket",
-                Key=ANY,
-            )
-        ]
-    )
-
-    assert result.exit_code == 0
-    assert "Completed uploading" in result.output
-
-
-def test_upload_osquery__calls_s3() -> None:
-    with patch_boto3_session() as mock_session:
-        sample_data_path = "etc/sample_data/osquery_data.log"
-        result = invoke_with_default_args(
-            [
-                "upload",
-                "osquery",
-                "--logfile",
-                sample_data_path,
-                "--log-bucket",
-                "fake-deployment-osquery-log-bucket",
-                "--queue-url",
-                "fake-osquery-queue",
-            ],
-        )
-
-    mock_s3_client = mock_session.client("s3")
-    mock_s3_client.put_object.assert_has_calls(
-        [
-            call(
-                Body=ANY,
-                Bucket="fake-deployment-osquery-log-bucket",
-                Key=ANY,
-            )
-        ]
-    )
-
-    assert result.exit_code == 0
-    assert "Completed uploading" in result.output
-
-
-def _mock_grapl_is_provisioned(
-    mock_session: BotoSessionMock, is_provisioned: bool
-) -> None:
-    table_instance = MagicMock("Mock DynamoDB table")
-    table_instance.scan = MagicMock(
-        return_value={
-            "Items": (
-                ["fool graplctl into thinking we're provisioned"]
-                if is_provisioned
-                else []
-            )
-        }
-    )
-    mock_session.resource("dynamodb").Table.return_value = table_instance
diff --git a/src/python/python-proto/python_proto/tests/strategies.py b/src/python/python-proto/python_proto/tests/strategies.py
index db7a233b2a..dfa7617541 100644
--- a/src/python/python-proto/python_proto/tests/strategies.py
+++ b/src/python/python-proto/python_proto/tests/strategies.py
@@ -9,6 +9,7 @@
     DecrementOnlyUintProp,
     Edge,
     EdgeList,
+    ExecutionHit,
     GraphDescription,
     IdentifiedGraph,
     IdentifiedNode,
@@ -18,6 +19,7 @@
     ImmutableUintProp,
     IncrementOnlyIntProp,
     IncrementOnlyUintProp,
+    Lens,
     MergedEdge,
     MergedEdgeList,
     MergedGraph,
@@ -62,6 +64,7 @@
 DURATION_NANOS_MAX = 10**9 - 1
 
 MAX_LIST_SIZE = 5
+MAX_DICT_SIZE = 5
 
 MIN_LOG_EVENT_SIZE = 0
 MAX_LOG_EVENT_SIZE = 1024
@@ -280,7 +283,7 @@ def node_properties(
 
 def node_descriptions(
     properties: st.SearchStrategy[Mapping[str, NodeProperty]] = st.dictionaries(
-        keys=st.text(), values=node_properties()
+        keys=st.text(), values=node_properties(), max_size=MAX_DICT_SIZE
     ),
     node_keys: st.SearchStrategy[str] = st.text(),
     node_types: st.SearchStrategy[str] = st.text(),
@@ -299,7 +302,7 @@ def node_descriptions(
 
 def identified_nodes(
     properties: st.SearchStrategy[Mapping[str, NodeProperty]] = st.dictionaries(
-        keys=st.text(), values=node_properties()
+        keys=st.text(), values=node_properties(), max_size=MAX_DICT_SIZE
     ),
     node_keys: st.SearchStrategy[str] = st.text(),
     node_types: st.SearchStrategy[str] = st.text(),
@@ -314,7 +317,7 @@ def identified_nodes(
 
 def merged_nodes(
     properties: st.SearchStrategy[Mapping[str, NodeProperty]] = st.dictionaries(
-        keys=st.text(), values=node_properties()
+        keys=st.text(), values=node_properties(), max_size=MAX_DICT_SIZE
     ),
     uids: st.SearchStrategy[int] = st.integers(
         min_value=UINT64_MIN, max_value=UINT64_MAX
@@ -385,10 +388,10 @@ def merged_edge_lists(
 
 def graph_descriptions(
     nodes: st.SearchStrategy[Mapping[str, NodeDescription]] = st.dictionaries(
-        keys=st.text(), values=node_descriptions()
+        keys=st.text(), values=node_descriptions(), max_size=MAX_DICT_SIZE
     ),
     edges: st.SearchStrategy[Mapping[str, EdgeList]] = st.dictionaries(
-        keys=st.text(), values=edge_lists()
+        keys=st.text(), values=edge_lists(), max_size=MAX_DICT_SIZE
     ),
 ) -> st.SearchStrategy[GraphDescription]:
     return st.builds(
@@ -400,10 +403,10 @@ def graph_descriptions(
 
 def identified_graphs(
     nodes: st.SearchStrategy[Mapping[str, IdentifiedNode]] = st.dictionaries(
-        keys=st.text(), values=identified_nodes()
+        keys=st.text(), values=identified_nodes(), max_size=MAX_DICT_SIZE
     ),
     edges: st.SearchStrategy[Mapping[str, EdgeList]] = st.dictionaries(
-        keys=st.text(), values=edge_lists()
+        keys=st.text(), values=edge_lists(), max_size=MAX_DICT_SIZE
     ),
 ) -> st.SearchStrategy[IdentifiedGraph]:
     return st.builds(
@@ -415,10 +418,10 @@ def identified_graphs(
 
 def merged_graphs(
     nodes: st.SearchStrategy[Mapping[str, MergedNode]] = st.dictionaries(
-        keys=st.text(), values=merged_nodes()
+        keys=st.text(), values=merged_nodes(), max_size=MAX_DICT_SIZE
     ),
     edges: st.SearchStrategy[Mapping[str, MergedEdgeList]] = st.dictionaries(
-        keys=st.text(), values=merged_edge_lists()
+        keys=st.text(), values=merged_edge_lists(), max_size=MAX_DICT_SIZE
     ),
 ) -> st.SearchStrategy[MergedGraph]:
     return st.builds(
@@ -428,6 +431,54 @@ def merged_graphs(
     )
 
 
+def lenses(
+    lens_types: st.SearchStrategy[str] = st.text(),
+    lens_names: st.SearchStrategy[str] = st.text(),
+    uids: st.SearchStrategy[int] = st.integers(
+        min_value=UINT64_MIN, max_value=UINT64_MAX
+    ),
+    scores: st.SearchStrategy[int] = st.integers(
+        min_value=UINT64_MIN, max_value=UINT64_MAX
+    ),
+) -> st.SearchStrategy[Lens]:
+    return st.builds(
+        Lens,
+        lens_type=lens_types,
+        lens_name=lens_names,
+        uid=uids,
+        score=scores,
+    )
+
+
+def execution_hits(
+    nodes: st.SearchStrategy[Mapping[str, MergedNode]] = st.dictionaries(
+        keys=st.text(), values=merged_nodes(), max_size=MAX_DICT_SIZE
+    ),
+    edges: st.SearchStrategy[Mapping[str, MergedEdgeList]] = st.dictionaries(
+        keys=st.text(), values=merged_edge_lists(), max_size=MAX_DICT_SIZE
+    ),
+    analyzer_names: st.SearchStrategy[str] = st.text(),
+    risk_scores: st.SearchStrategy[int] = st.integers(
+        min_value=UINT64_MIN, max_value=UINT64_MAX
+    ),
+    lenses: st.SearchStrategy[Sequence[Lens]] = st.lists(
+        lenses(), max_size=MAX_LIST_SIZE
+    ),
+    risky_node_keys: st.SearchStrategy[Sequence[str]] = st.lists(
+        st.text(), max_size=MAX_LIST_SIZE
+    ),
+) -> st.SearchStrategy[ExecutionHit]:
+    return st.builds(
+        ExecutionHit,
+        nodes=nodes,
+        edges=edges,
+        analyzer_name=analyzer_names,
+        risk_score=risk_scores,
+        lenses=lenses,
+        risky_node_keys=risky_node_keys,
+    )
+
+
 #
 # metrics
 #
diff --git a/src/python/python-proto/python_proto/tests/test_api.py b/src/python/python-proto/python_proto/tests/test_api.py
index ae17911406..98cc2f27a1 100644
--- a/src/python/python-proto/python_proto/tests/test_api.py
+++ b/src/python/python-proto/python_proto/tests/test_api.py
@@ -8,6 +8,7 @@
     decrement_only_uint_props,
     edge_lists,
     edges,
+    execution_hits,
     graph_descriptions,
     id_strategies,
     identified_graphs,
@@ -17,6 +18,7 @@
     immutable_uint_props,
     increment_only_int_props,
     increment_only_uint_props,
+    lenses,
     merged_edge_lists,
     merged_edges,
     merged_graphs,
@@ -44,6 +46,10 @@ def test_edge_encode_decode() -> None:
     check_encode_decode_invariant(edges())
 
 
+def test_execution_hit_encode_decode() -> None:
+    check_encode_decode_invariant(execution_hits())
+
+
 def test_graph_description_encode_decode() -> None:
     check_encode_decode_invariant(graph_descriptions())
 
@@ -80,6 +86,10 @@ def test_increment_only_uint_prop_encode_decode() -> None:
     check_encode_decode_invariant(increment_only_uint_props())
 
 
+def test_lens_encode_decode() -> None:
+    check_encode_decode_invariant(lenses())
+
+
 def test_merged_edge_list_encode_decode() -> None:
     check_encode_decode_invariant(merged_edge_lists())
 
diff --git a/src/rust/derive-dynamic-node/tests/derive_session.rs b/src/rust/derive-dynamic-node/tests/derive_session.rs
index 92fec4a1ac..37cd07e0a8 100644
--- a/src/rust/derive-dynamic-node/tests/derive_session.rs
+++ b/src/rust/derive-dynamic-node/tests/derive_session.rs
@@ -2,7 +2,7 @@ use derive_dynamic_node::{
     GraplSessionId,
     NodeDescription,
 };
-use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+use rust_proto::graplinc::grapl::api::graph::v1beta1::{
     IdStrategy,
     ImmutableUintProp,
     NodeDescription,
diff --git a/src/rust/event-source/tests/test_crud_event_source.rs b/src/rust/event-source/tests/test_crud_event_source.rs
index f113ee90fd..e02d299c96 100644
--- a/src/rust/event-source/tests/test_crud_event_source.rs
+++ b/src/rust/event-source/tests/test_crud_event_source.rs
@@ -1,10 +1,10 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::time::SystemTime;
 
 use event_source::client::FromEnv;
 use grapl_utils::future_ext::GraplFutureExt;
-use rust_proto_new::graplinc::grapl::api::event_source::v1beta1 as es_api;
+use rust_proto::graplinc::grapl::api::event_source::v1beta1 as es_api;
 
 #[test_log::test(tokio::test)]
 async fn test_create_update_get() -> Result<(), Box<dyn std::error::Error>> {
diff --git a/src/rust/generator-dispatcher/tests/generator_dispatcher_integration_tests.rs b/src/rust/generator-dispatcher/tests/generator_dispatcher_integration_tests.rs
index 8494c89c04..a45ffbf9bb 100644
--- a/src/rust/generator-dispatcher/tests/generator_dispatcher_integration_tests.rs
+++ b/src/rust/generator-dispatcher/tests/generator_dispatcher_integration_tests.rs
@@ -1,10 +1,10 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::time::Duration;
 
 use bytes::Bytes;
 use plugin_work_queue::test_utils::PsqlQueueTestExtensions;
-use rust_proto_new::graplinc::grapl::api::pipeline_ingress::v1beta1::PublishRawLogRequest;
+use rust_proto::graplinc::grapl::api::pipeline_ingress::v1beta1::PublishRawLogRequest;
 use test_context::test_context;
 use tracing::Instrument;
 use uuid::Uuid;
diff --git a/src/rust/generator-dispatcher/tests/test_utils/context.rs b/src/rust/generator-dispatcher/tests/test_utils/context.rs
index 2e7610c84f..6efb0635df 100644
--- a/src/rust/generator-dispatcher/tests/test_utils/context.rs
+++ b/src/rust/generator-dispatcher/tests/test_utils/context.rs
@@ -9,7 +9,7 @@ use plugin_work_queue::{
     psql_queue::PsqlQueue,
     PluginWorkQueueDbConfig,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::api::pipeline_ingress::v1beta1::client::PipelineIngressClient,
     protocol::{
         healthcheck::client::HealthcheckClient,
diff --git a/src/rust/generators/sysmon-generator/tests/integration_test.rs b/src/rust/generators/sysmon-generator/tests/integration_test.rs
index 632b857f92..fee0816dd4 100644
--- a/src/rust/generators/sysmon-generator/tests/integration_test.rs
+++ b/src/rust/generators/sysmon-generator/tests/integration_test.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::time::Duration;
 
@@ -14,7 +14,7 @@ use opentelemetry::{
     global,
     sdk::propagation::TraceContextPropagator,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::{
         api::{
             graph::v1beta1::{
diff --git a/src/rust/graph-merger/tests/integration_test.rs b/src/rust/graph-merger/tests/integration_test.rs
index 41cd1f3e6e..2489eb30f1 100644
--- a/src/rust/graph-merger/tests/integration_test.rs
+++ b/src/rust/graph-merger/tests/integration_test.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::time::Duration;
 
@@ -14,7 +14,7 @@ use opentelemetry::{
     global,
     sdk::propagation::TraceContextPropagator,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::{
         api::{
             graph::v1beta1::{
diff --git a/src/rust/graph-merger/tests/test_merging.rs b/src/rust/graph-merger/tests/test_merging.rs
index 5299d44b00..a9161a57a8 100644
--- a/src/rust/graph-merger/tests/test_merging.rs
+++ b/src/rust/graph-merger/tests/test_merging.rs
@@ -1,4 +1,4 @@
-#[cfg(feature = "new_integration_tests")]
+#[cfg(feature = "integration_tests")]
 pub mod test {
     use std::{
         collections::HashMap,
@@ -35,7 +35,7 @@ pub mod test {
         Query,
     };
     use graph_merger::upserter::GraphMergeHelper;
-    use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+    use rust_proto::graplinc::grapl::api::graph::v1beta1::{
         IdentifiedGraph,
         IdentifiedNode,
         ImmutableStrProp,
diff --git a/src/rust/model-plugin-deployer/tests/test_deploy_model.rs b/src/rust/model-plugin-deployer/tests/test_deploy_model.rs
index 26620d5c47..d371f35e92 100644
--- a/src/rust/model-plugin-deployer/tests/test_deploy_model.rs
+++ b/src/rust/model-plugin-deployer/tests/test_deploy_model.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "integration")]
+#![cfg(feature = "integration_tests")]
 
 use model_plugin_deployer::client::{
     DeployModelRequest,
diff --git a/src/rust/node-identifier/tests/integration_test.rs b/src/rust/node-identifier/tests/integration_test.rs
index 921dad9a11..360106e925 100644
--- a/src/rust/node-identifier/tests/integration_test.rs
+++ b/src/rust/node-identifier/tests/integration_test.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::time::Duration;
 
@@ -14,7 +14,7 @@ use opentelemetry::{
     global,
     sdk::propagation::TraceContextPropagator,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::{
         api::{
             graph::v1beta1::{
diff --git a/src/rust/node-identifier/tests/sessiondb_integration_tests.rs b/src/rust/node-identifier/tests/sessiondb_integration_tests.rs
index 9628e6ef18..8e8f9a06fe 100644
--- a/src/rust/node-identifier/tests/sessiondb_integration_tests.rs
+++ b/src/rust/node-identifier/tests/sessiondb_integration_tests.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::time::Duration;
 
diff --git a/src/rust/organization-management/tests/test_create_organization.rs b/src/rust/organization-management/tests/test_create_organization.rs
index 2dbef1e4f5..76a30aef8f 100644
--- a/src/rust/organization-management/tests/test_create_organization.rs
+++ b/src/rust/organization-management/tests/test_create_organization.rs
@@ -1,12 +1,17 @@
-#![cfg(feature = "integration")]
+#![cfg(feature = "integration_tests")]
+
+use std::time::Duration;
 
 use clap::Parser;
 use grapl_utils::future_ext::GraplFutureExt;
-use organization_management::{
-    client::OrganizationManagementServiceClient,
-    OrganizationManagementServiceConfig,
+use organization_management::OrganizationManagementServiceConfig;
+use rust_proto::{
+    graplinc::grapl::api::organization_management::v1beta1::{
+        client::OrganizationManagementClient,
+        CreateOrganizationRequest,
+    },
+    protocol::healthcheck::client::HealthcheckClient,
 };
-use rust_proto::organization_management::CreateOrganizationRequest;
 
 #[test_log::test(tokio::test)]
 async fn test_create_organization() -> Result<(), Box<dyn std::error::Error>> {
@@ -25,10 +30,27 @@ async fn test_create_organization() -> Result<(), Box<dyn std::error::Error>> {
     );
 
     let pool = sqlx::PgPool::connect(&postgres_address)
-        .timeout(std::time::Duration::from_secs(5))
+        .timeout(Duration::from_secs(5))
         .await??;
 
-    let mut client = OrganizationManagementServiceClient::from_env().await?;
+    let endpoint = std::env::var("ORGANIZATION_MANAGEMENT_CLIENT_ADDRESS")
+        .expect("missing environment variable ORGANIZATION_MANAGEMENT_CLIENT_ADDRESS");
+
+    tracing::info!(
+        message = "waiting 60s for organization-management to report healthy",
+        endpoint = %endpoint,
+    );
+
+    HealthcheckClient::wait_until_healthy(
+        endpoint.clone(),
+        "graplinc.grapl.api.organization_management.v1beta1.OrganizationManagementService",
+        Duration::from_secs(60),
+        Duration::from_millis(500),
+    )
+    .await
+    .expect("organization-management never reported healthy");
+
+    let mut client = OrganizationManagementClient::connect(endpoint.clone()).await?;
 
     let organization_display_name = uuid::Uuid::new_v4().to_string();
     let admin_username = "test user".to_string();
@@ -39,14 +61,11 @@ async fn test_create_organization() -> Result<(), Box<dyn std::error::Error>> {
         organization_display_name: organization_display_name.clone(),
         admin_username,
         admin_email: admin_email.parse().unwrap(),
-        admin_password,
+        admin_password: admin_password.into(),
         should_reset_password: false,
     };
 
-    let response = client
-        .create_organization(request)
-        .timeout(std::time::Duration::from_secs(5))
-        .await??;
+    let response = client.create_organization(request).await?;
 
     let organization_id = response.organization_id;
 
diff --git a/src/rust/organization-management/tests/test_create_user.rs b/src/rust/organization-management/tests/test_create_user.rs
index f89e67808a..ac4cd0f168 100644
--- a/src/rust/organization-management/tests/test_create_user.rs
+++ b/src/rust/organization-management/tests/test_create_user.rs
@@ -1,14 +1,17 @@
-#![cfg(feature = "integration")]
+#![cfg(feature = "integration_tests")]
+
+use std::time::Duration;
 
 use clap::Parser;
 use grapl_utils::future_ext::GraplFutureExt;
-use organization_management::{
-    client::OrganizationManagementServiceClient,
-    OrganizationManagementServiceConfig,
-};
-use rust_proto::organization_management::{
-    CreateOrganizationRequest,
-    CreateUserRequest,
+use organization_management::OrganizationManagementServiceConfig;
+use rust_proto::{
+    graplinc::grapl::api::organization_management::v1beta1::{
+        client::OrganizationManagementClient,
+        CreateOrganizationRequest,
+        CreateUserRequest,
+    },
+    protocol::healthcheck::client::HealthcheckClient,
 };
 
 #[test_log::test(tokio::test)]
@@ -28,10 +31,28 @@ async fn test_create_user() -> Result<(), Box<dyn std::error::Error>> {
     );
 
     let pool = sqlx::PgPool::connect(&postgres_address)
-        .timeout(std::time::Duration::from_secs(5))
+        .timeout(Duration::from_secs(5))
         .await??;
 
-    let mut client = OrganizationManagementServiceClient::from_env().await?;
+    let endpoint = std::env::var("ORGANIZATION_MANAGEMENT_CLIENT_ADDRESS")
+        .expect("missing environment variable ORGANIZATION_MANAGEMENT_CLIENT_ADDRESS");
+
+    tracing::info!(
+        message = "waiting 60s for organization-management to report healthy",
+        endpoint = %endpoint,
+    );
+
+    HealthcheckClient::wait_until_healthy(
+        endpoint.clone(),
+        "graplinc.grapl.api.organization_management.v1beta1.OrganizationManagementService",
+        Duration::from_secs(60),
+        Duration::from_millis(500),
+    )
+    .await
+    .expect("organization-management never reported healthy");
+
+    let mut client = OrganizationManagementClient::connect(endpoint.clone()).await?;
+
     // create organization with admin user
     let organization_display_name = uuid::Uuid::new_v4().to_string();
     let admin_username = "test user".to_string();
@@ -42,14 +63,11 @@ async fn test_create_user() -> Result<(), Box<dyn std::error::Error>> {
         organization_display_name: organization_display_name.clone(),
         admin_username,
         admin_email: admin_email.parse().unwrap(),
-        admin_password,
+        admin_password: admin_password.into(),
         should_reset_password: false,
     };
 
-    let response = client
-        .create_organization(request)
-        .timeout(std::time::Duration::from_secs(5))
-        .await??;
+    let response = client.create_organization(request).await?;
 
     let organization_id = response.organization_id;
 
@@ -62,13 +80,10 @@ async fn test_create_user() -> Result<(), Box<dyn std::error::Error>> {
         organization_id,
         name,
         email: email.parse().unwrap(),
-        password,
+        password: password.into(),
     };
 
-    let response = client
-        .create_user(request)
-        .timeout(std::time::Duration::from_secs(5))
-        .await??;
+    let response = client.create_user(request).await?;
 
     let user_id = response.user_id;
 
diff --git a/src/rust/pipeline-ingress/tests/integration_test.rs b/src/rust/pipeline-ingress/tests/integration_test.rs
index c6d1c57e21..ad844d3345 100644
--- a/src/rust/pipeline-ingress/tests/integration_test.rs
+++ b/src/rust/pipeline-ingress/tests/integration_test.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::time::Duration;
 
@@ -14,7 +14,7 @@ use opentelemetry::{
     global,
     sdk::propagation::TraceContextPropagator,
 };
-use rust_proto_new::{
+use rust_proto::{
     graplinc::grapl::{
         api::pipeline_ingress::v1beta1::{
             client::PipelineIngressClient,
diff --git a/src/rust/plugin-registry/tests/test_create_plugin.rs b/src/rust/plugin-registry/tests/test_create_plugin.rs
index 00ce268c2e..6fd54bd095 100644
--- a/src/rust/plugin-registry/tests/test_create_plugin.rs
+++ b/src/rust/plugin-registry/tests/test_create_plugin.rs
@@ -1,8 +1,8 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use grapl_utils::future_ext::GraplFutureExt;
 use plugin_registry::client::FromEnv;
-use rust_proto_new::graplinc::grapl::api::plugin_registry::v1beta1::{
+use rust_proto::graplinc::grapl::api::plugin_registry::v1beta1::{
     CreatePluginRequestMetadata,
     GetPluginRequest,
     GetPluginResponse,
diff --git a/src/rust/plugin-registry/tests/test_deploy_plugin.rs b/src/rust/plugin-registry/tests/test_deploy_plugin.rs
index 57ad2adc6b..37d084811f 100644
--- a/src/rust/plugin-registry/tests/test_deploy_plugin.rs
+++ b/src/rust/plugin-registry/tests/test_deploy_plugin.rs
@@ -1,8 +1,8 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use grapl_utils::future_ext::GraplFutureExt;
 use plugin_registry::client::FromEnv;
-use rust_proto_new::graplinc::grapl::api::plugin_registry::v1beta1::{
+use rust_proto::graplinc::grapl::api::plugin_registry::v1beta1::{
     CreatePluginRequestMetadata,
     DeployPluginRequest,
     PluginRegistryServiceClient,
diff --git a/src/rust/plugin-registry/tests/test_nomad_client.rs b/src/rust/plugin-registry/tests/test_nomad_client.rs
index 9ff4ba760e..eb55588419 100644
--- a/src/rust/plugin-registry/tests/test_nomad_client.rs
+++ b/src/rust/plugin-registry/tests/test_nomad_client.rs
@@ -1,4 +1,4 @@
-#![cfg(feature = "new_integration_tests")]
+#![cfg(feature = "integration_tests")]
 
 use std::collections::HashMap;
 
diff --git a/src/rust/rust-proto-new/tests/pipeline_ingress_grpc_tests.rs b/src/rust/rust-proto/tests/pipeline_ingress_grpc_tests.rs
similarity index 99%
rename from src/rust/rust-proto-new/tests/pipeline_ingress_grpc_tests.rs
rename to src/rust/rust-proto/tests/pipeline_ingress_grpc_tests.rs
index fb48d7a600..7f869860fa 100644
--- a/src/rust/rust-proto-new/tests/pipeline_ingress_grpc_tests.rs
+++ b/src/rust/rust-proto/tests/pipeline_ingress_grpc_tests.rs
@@ -1,5 +1,5 @@
 use futures::channel::oneshot::Sender;
-use rust_proto_new::{
+use rust_proto::{
     graplinc::{
         common::v1beta1::{
             Duration,
diff --git a/src/rust/rust-proto-new/tests/plugin_registry_grpc_tests.rs b/src/rust/rust-proto/tests/plugin_registry_grpc_tests.rs
similarity index 100%
rename from src/rust/rust-proto-new/tests/plugin_registry_grpc_tests.rs
rename to src/rust/rust-proto/tests/plugin_registry_grpc_tests.rs
diff --git a/src/rust/rust-proto-new/tests/proto_serde_tests.rs b/src/rust/rust-proto/tests/proto_serde_tests.rs
similarity index 97%
rename from src/rust/rust-proto-new/tests/proto_serde_tests.rs
rename to src/rust/rust-proto/tests/proto_serde_tests.rs
index ebd9bbdc6d..7d928dc52f 100644
--- a/src/rust/rust-proto-new/tests/proto_serde_tests.rs
+++ b/src/rust/rust-proto/tests/proto_serde_tests.rs
@@ -1,6 +1,6 @@
 mod test_utils;
 use proptest::prelude::*;
-use rust_proto_new::graplinc::common::v1beta1::{
+use rust_proto::graplinc::common::v1beta1::{
     Duration,
     SystemTime,
 };
@@ -229,6 +229,20 @@ mod graph {
         ) {
             check_encode_decode_invariant(merged_graph)
         }
+
+        #[test]
+        fn test_lens_encode_decode(
+            lens in st::lenses()
+        ) {
+            check_encode_decode_invariant(lens)
+        }
+
+        #[test]
+        fn test_execution_hit_encode_decode(
+            execution_hit in st::execution_hits()
+        ) {
+            check_encode_decode_invariant(execution_hit)
+        }
     }
 }
 
@@ -248,11 +262,6 @@ mod pipeline {
             check_encode_decode_invariant(raw_log)
         }
 
-        #[test]
-        fn test_v1_envelope_encode_decode(envelope in st::v1_envelopes()) {
-            check_encode_decode_invariant(envelope)
-        }
-
         #[test]
         fn test_uuid_envelope_encode_decode(
             envelope in st::envelopes(strategies::uuids())
diff --git a/src/rust/rust-proto-new/tests/test_utils/mod.rs b/src/rust/rust-proto/tests/test_utils/mod.rs
similarity index 100%
rename from src/rust/rust-proto-new/tests/test_utils/mod.rs
rename to src/rust/rust-proto/tests/test_utils/mod.rs
diff --git a/src/rust/rust-proto-new/tests/test_utils/serde.rs b/src/rust/rust-proto/tests/test_utils/serde.rs
similarity index 97%
rename from src/rust/rust-proto-new/tests/test_utils/serde.rs
rename to src/rust/rust-proto/tests/test_utils/serde.rs
index 43d0bc65e0..aac435ce37 100644
--- a/src/rust/rust-proto-new/tests/test_utils/serde.rs
+++ b/src/rust/rust-proto/tests/test_utils/serde.rs
@@ -1,6 +1,6 @@
 use std::fmt::Debug;
 
-use rust_proto_new::{
+use rust_proto::{
     SerDe,
     SerDeError,
 };
diff --git a/src/rust/rust-proto-new/tests/test_utils/strategies.rs b/src/rust/rust-proto/tests/test_utils/strategies.rs
similarity index 93%
rename from src/rust/rust-proto-new/tests/test_utils/strategies.rs
rename to src/rust/rust-proto/tests/test_utils/strategies.rs
index a694efe344..75a3859e8d 100644
--- a/src/rust/rust-proto-new/tests/test_utils/strategies.rs
+++ b/src/rust/rust-proto/tests/test_utils/strategies.rs
@@ -42,10 +42,9 @@ where
 pub mod pipeline {
     use std::fmt::Debug;
 
-    use rust_proto_new::{
+    use rust_proto::{
         graplinc::grapl::pipeline::{
             v1beta1::{
-                Envelope as EnvelopeV1,
                 Metadata,
                 RawLog,
             },
@@ -98,20 +97,6 @@ pub mod pipeline {
     // Envelope
     //
 
-    prop_compose! {
-        pub fn v1_envelopes()(
-            metadata in metadatas(),
-            inner_type in any::<String>(),
-            inner_message in bytes(256),
-        ) -> EnvelopeV1 {
-            EnvelopeV1 {
-                metadata,
-                inner_type,
-                inner_message
-            }
-        }
-    }
-
     pub fn envelopes<T>(
         inner_strategy: impl Strategy<Value = T>,
     ) -> impl Strategy<Value = Envelope<T>>
@@ -129,11 +114,12 @@ pub mod pipeline {
 
 pub mod graph {
     use proptest::collection;
-    use rust_proto_new::graplinc::grapl::api::graph::v1beta1::{
+    use rust_proto::graplinc::grapl::api::graph::v1beta1::{
         DecrementOnlyIntProp,
         DecrementOnlyUintProp,
         Edge,
         EdgeList,
+        ExecutionHit,
         GraphDescription,
         IdStrategy,
         IdentifiedGraph,
@@ -143,6 +129,7 @@ pub mod graph {
         ImmutableUintProp,
         IncrementOnlyIntProp,
         IncrementOnlyUintProp,
+        Lens,
         MergedEdge,
         MergedEdgeList,
         MergedGraph,
@@ -255,6 +242,26 @@ pub mod graph {
         }
     }
 
+    //
+    // Lens
+    //
+
+    prop_compose! {
+        pub fn lenses()(
+            lens_type in any::<String>(),
+            lens_name in any::<String>(),
+            uid in any::<u64>(),
+            score in any::<u64>(),
+        ) -> Lens {
+            Lens {
+                lens_type,
+                lens_name,
+                uid: Some(uid),
+                score: Some(score)
+            }
+        }
+    }
+
     //
     // Edge
     //
@@ -287,6 +294,30 @@ pub mod graph {
         }
     }
 
+    //
+    // ExecutionHit
+    //
+
+    prop_compose! {
+        pub fn execution_hits()(
+            nodes in collection::hash_map(any::<String>(), merged_nodes(), 10),
+            edges in collection::hash_map(any::<String>(), merged_edge_lists(), 10),
+            analyzer_name in any::<String>(),
+            risk_score in any::<u64>(),
+            lenses in collection::vec(lenses(), 10),
+            risky_node_keys in collection::vec(any::<String>(), 10)
+        ) -> ExecutionHit {
+            ExecutionHit{
+                nodes,
+                edges,
+                analyzer_name,
+                risk_score,
+                lenses,
+                risky_node_keys
+            }
+        }
+    }
+
     //
     // Session
     //
@@ -518,7 +549,7 @@ pub mod graph {
 }
 
 pub mod pipeline_ingress {
-    use rust_proto_new::graplinc::grapl::api::pipeline_ingress::v1beta1::{
+    use rust_proto::graplinc::grapl::api::pipeline_ingress::v1beta1::{
         PublishRawLogRequest,
         PublishRawLogResponse,
     };
@@ -559,7 +590,7 @@ pub mod pipeline_ingress {
 }
 
 pub mod event_source {
-    use rust_proto_new::graplinc::grapl::api::event_source::v1beta1 as native;
+    use rust_proto::graplinc::grapl::api::event_source::v1beta1 as native;
 
     use super::*;
 
@@ -661,7 +692,7 @@ pub mod event_source {
 }
 
 pub mod plugin_registry {
-    use rust_proto_new::graplinc::grapl::api::plugin_registry::v1beta1::{
+    use rust_proto::graplinc::grapl::api::plugin_registry::v1beta1::{
         CreatePluginRequest,
         CreatePluginRequestMetadata,
         CreatePluginResponse,
@@ -828,7 +859,7 @@ pub mod plugin_registry {
 }
 
 pub mod plugin_sdk_generators {
-    use rust_proto_new::graplinc::grapl::api::plugin_sdk::generators::v1beta1 as native;
+    use rust_proto::graplinc::grapl::api::plugin_sdk::generators::v1beta1 as native;
 
     use super::*;
 
@@ -864,7 +895,7 @@ pub mod plugin_sdk_generators {
 }
 
 pub mod plugin_work_queue {
-    use rust_proto_new::graplinc::grapl::api::plugin_work_queue::v1beta1 as native;
+    use rust_proto::graplinc::grapl::api::plugin_work_queue::v1beta1 as native;
 
     use super::*;
 
diff --git a/src/rust/sqs-executor/tests/redis_cache.rs b/src/rust/sqs-executor/tests/redis_cache.rs
deleted file mode 100644
index 06568f09ed..0000000000
--- a/src/rust/sqs-executor/tests/redis_cache.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-#![cfg(feature = "integration")]
-
-use grapl_observe::metric_reporter::MetricReporter;
-use sqs_executor::cache::Cache;
-
-#[tokio::test]
-async fn redis_cache() {
-    const LRU_SIZE: usize = 5;
-    const TOTAL_SIZE: usize = 10;
-
-    // Create a set of cacheables that we'll store in the cache
-    let all_cacheables: Vec<String> = (0..TOTAL_SIZE)
-        .map(|i| format!("sqs-executor::tests::redis_cache-{}", i))
-        .collect();
-    let all_cacheables = all_cacheables.as_slice();
-    let (stored, not_stored) = all_cacheables.split_at(LRU_SIZE);
-
-    let redis_endpoint = std::env::var("REDIS_ENDPOINT").expect("REDIS_ENDPOINT");
-
-    let mut cache = sqs_executor::redis_cache::RedisCache::with_lru_capacity(
-        LRU_SIZE,
-        redis_endpoint,
-        MetricReporter::<std::io::Stdout>::new("redis_cache"),
-    )
-    .await
-    .expect("redis client");
-
-    cache.store_all(stored).await.unwrap();
-
-    // When calling `filter_cached` on what we've stored, we expect an empty response.
-    assert_eq!(cache.filter_cached(stored).await, Vec::new() as Vec<String>);
-    assert_eq!(cache.filter_cached(not_stored).await, not_stored);
-    assert_eq!(cache.filter_cached(all_cacheables).await, not_stored);
-
-    assert!(cache.all_exist(stored).await);
-    assert!(!cache.all_exist(not_stored).await);
-    assert!(!cache.all_exist(all_cacheables).await);
-
-    assert!(!cache.all_exist(&["non-existent-key"]).await);
-}