Merge "Scale vision uncertainty by distance for mapping"
diff --git a/.bazelrc b/.bazelrc
index ce727ec..831889e 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -6,6 +6,7 @@
 
 # Default to py3 since that's all we support
 build --python_version=PY3
+build --incompatible_default_to_explicit_init_py
 
 # For now we only support building on x86 Linux so we can hard-code the host
 # platform.
@@ -21,7 +22,7 @@
 
 # Shortcuts for selecting the target platform.
 build:k8 --platforms=//tools/platforms:linux_x86
-build:k8_upstream_python --platforms=//tools/platforms:linux_x86_upstream_python --host_platform=//tools/platforms:linux_x86_upstream_python
+build:k8_legacy_python --platforms=//tools/platforms:linux_x86_legacy_python --host_platform=//tools/platforms:linux_x86_legacy_python
 build:roborio --platforms=//tools/platforms:linux_roborio
 build:roborio --platform_suffix=-roborio
 build:armv7 --platforms=//tools/platforms:linux_armv7
diff --git a/BUILD b/BUILD
index 331c4b6..a568e20 100644
--- a/BUILD
+++ b/BUILD
@@ -11,6 +11,7 @@
 # gazelle:go_generate_proto false
 # gazelle:exclude third_party
 # gazelle:exclude external
+# gazelle:resolve go github.com/google/flatbuffers/go @com_github_google_flatbuffers//go:go_default_library
 # gazelle:resolve go github.com/phst/runfiles @com_github_phst_runfiles//:go_default_library
 # gazelle:resolve go github.com/frc971/971-Robot-Code/build_tests/fbs //build_tests:test_go_fbs
 # gazelle:resolve go github.com/frc971/971-Robot-Code/scouting/webserver/requests/messages/error_response //scouting/webserver/requests/messages:error_response_go_fbs
diff --git a/WORKSPACE b/WORKSPACE
index 3158eb8..3ed0f2b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,10 +3,91 @@
 load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
 load("@bazel_tools//tools/jdk:remote_java_repository.bzl", "remote_java_repository")
-load(
-    "//debian:python.bzl",
-    python_debs = "files",
+load("//tools/ci:repo_defs.bzl", "ci_configure")
+
+ci_configure(name = "ci_configure")
+
+load("@ci_configure//:ci.bzl", "RUNNING_IN_CI")
+
+http_archive(
+    name = "platforms",
+    sha256 = "2c8d8347427e6bb0ba7cf9f933c08fe2be2b62ff2454546ad852f7bf267aad87",
+    strip_prefix = "platforms-e658a6af526089406d0057160542597501ba65d7",
+    url = "https://github.com/bazelbuild/platforms/archive/e658a6af526089406d0057160542597501ba65d7.zip",
 )
+
+http_archive(
+    name = "bazel_skylib",
+    sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c",
+    urls = [
+        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
+        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
+    ],
+)
+
+http_archive(
+    name = "rules_python",
+    patch_args = ["-p1"],
+    patches = [
+        "//third_party:rules_python/0001-Support-overriding-individual-packages.patch",
+        "//third_party:rules_python/0002-Allow-user-to-patch-wheels.patch",
+    ],
+    sha256 = "497ca47374f48c8b067d786b512ac10a276211810f4a580178ee9b9ad139323a",
+    strip_prefix = "rules_python-0.16.1",
+    url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.16.1.tar.gz",
+)
+
+load("@rules_python//python:repositories.bzl", "python_register_toolchains")
+
+python_register_toolchains(
+    name = "python3_9",
+    python_version = "3.9",
+    register_toolchains = False,
+)
+
+load("@python3_9//:defs.bzl", python_interpreter = "interpreter")
+load("@rules_python//python:pip.bzl", "pip_parse")
+load("//tools/python:package_annotations.bzl", PYTHON_ANNOTATIONS = "ANNOTATIONS")
+
+pip_parse(
+    name = "pip_deps",
+    annotations = PYTHON_ANNOTATIONS,
+    enable_implicit_namespace_pkgs = True,
+    overrides = "//tools/python:whl_overrides.json",
+    patch_spec = "//tools/python:patches.json",
+    python_interpreter_target = python_interpreter,
+    require_overrides = RUNNING_IN_CI,
+    requirements_lock = "//tools/python:requirements.lock.txt",
+)
+
+# Load the starlark macro which will define your dependencies.
+load("@pip_deps//:requirements.bzl", install_pip_deps = "install_deps")
+
+install_pip_deps()
+
+load("//tools/python:repo_defs.bzl", "pip_configure")
+
+pip_configure(
+    name = "pip",
+)
+
+http_archive(
+    name = "rules_pkg",
+    patch_args = ["-p1"],
+    patches = [
+        "//third_party:rules_pkg/0001-Fix-tree-artifacts.patch",
+    ],
+    sha256 = "62eeb544ff1ef41d786e329e1536c1d541bb9bcad27ae984d57f18f314018e66",
+    urls = [
+        "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
+        "https://github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
+    ],
+)
+
+load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
+
+rules_pkg_dependencies()
+
 load(
     "//debian:apache2.bzl",
     apache2_debs = "files",
@@ -44,18 +125,10 @@
     patchelf_debs = "files",
 )
 load(
-    "//debian:matplotlib.bzl",
-    matplotlib_debs = "files",
-)
-load(
     "//debian:arm_frc_gnueabi_deps.bzl",
     arm_frc_gnueabi_deps_debs = "files",
 )
 load(
-    "//debian:python_gtk.bzl",
-    python_gtk_debs = "files",
-)
-load(
     "//debian:gtk_runtime.bzl",
     gtk_runtime_debs = "files",
 )
@@ -105,8 +178,6 @@
 )
 load("//debian:packages.bzl", "generate_repositories_for_debs")
 
-generate_repositories_for_debs(python_debs)
-
 generate_repositories_for_debs(rsync_debs)
 
 generate_repositories_for_debs(ssh_debs)
@@ -125,12 +196,8 @@
 
 generate_repositories_for_debs(patchelf_debs)
 
-generate_repositories_for_debs(matplotlib_debs)
-
 generate_repositories_for_debs(arm_frc_gnueabi_deps_debs)
 
-generate_repositories_for_debs(python_gtk_debs)
-
 generate_repositories_for_debs(gtk_runtime_debs)
 
 generate_repositories_for_debs(opencv_arm64_debs)
@@ -299,7 +366,6 @@
     # Find a good way to select between these two M4F toolchains.
     #"//tools/cpp:cc-toolchain-cortex-m4f-k22",
     "//tools/python:python_toolchain",
-    "//tools/python:upstream_python_toolchain",
     "//tools/go:noop_go_toolchain",
     "//tools/rust:rust-toolchain-x86",
     "//tools/rust:rust-toolchain-armv7",
@@ -311,35 +377,6 @@
     "//tools/ts:noop_node_toolchain",
 )
 
-load("//tools/ci:repo_defs.bzl", "ci_configure")
-
-ci_configure(name = "ci_configure")
-
-load("@ci_configure//:ci.bzl", "RUNNING_IN_CI")
-
-http_archive(
-    name = "platforms",
-    sha256 = "2c8d8347427e6bb0ba7cf9f933c08fe2be2b62ff2454546ad852f7bf267aad87",
-    strip_prefix = "platforms-e658a6af526089406d0057160542597501ba65d7",
-    url = "https://github.com/bazelbuild/platforms/archive/e658a6af526089406d0057160542597501ba65d7.zip",
-)
-
-http_archive(
-    name = "bazel_skylib",
-    sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
-        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
-    ],
-)
-
-http_archive(
-    name = "python_repo",
-    build_file = "@//debian:python.BUILD",
-    sha256 = "048c51872f9c3853ae4e961c710533f477194a3f170b454e8d582f32a83e90f5",
-    url = "https://www.frc971.org/Build-Dependencies/python-6.tar.gz",
-)
-
 http_archive(
     name = "com_github_stevengj_nlopt",
     build_file = "@//debian:nlopt.BUILD",
@@ -409,52 +446,6 @@
     ],
 )
 
-http_archive(
-    name = "rules_python",
-    patch_args = ["-p1"],
-    patches = [
-        "//third_party:rules_python/0001-Support-overriding-individual-packages.patch",
-        "//third_party:rules_python/0002-Allow-user-to-patch-wheels.patch",
-    ],
-    sha256 = "497ca47374f48c8b067d786b512ac10a276211810f4a580178ee9b9ad139323a",
-    strip_prefix = "rules_python-0.16.1",
-    url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.16.1.tar.gz",
-)
-
-load("@rules_python//python:repositories.bzl", "python_register_toolchains")
-
-python_register_toolchains(
-    name = "python3_9",
-    python_version = "3.9",
-    register_toolchains = False,
-)
-
-load("@python3_9//:defs.bzl", python_interpreter = "interpreter")
-load("@rules_python//python:pip.bzl", "pip_parse")
-load("//tools/python:package_annotations.bzl", PYTHON_ANNOTATIONS = "ANNOTATIONS")
-
-pip_parse(
-    name = "pip_deps",
-    annotations = PYTHON_ANNOTATIONS,
-    enable_implicit_namespace_pkgs = True,
-    overrides = "//tools/python:whl_overrides.json",
-    patch_spec = "//tools/python:patches.json",
-    python_interpreter_target = python_interpreter,
-    require_overrides = RUNNING_IN_CI,
-    requirements_lock = "//tools/python:requirements.lock.txt",
-)
-
-# Load the starlark macro which will define your dependencies.
-load("@pip_deps//:requirements.bzl", install_pip_deps = "install_deps")
-
-install_pip_deps()
-
-load("//tools/python:repo_defs.bzl", "pip_configure")
-
-pip_configure(
-    name = "pip",
-)
-
 new_local_repository(
     name = "usr_repo",
     build_file = "@//debian:usr.BUILD",
@@ -513,38 +504,11 @@
     url = "https://www.frc971.org/Build-Dependencies/2022-01-06-debian-bullseye_rootfs.tar.bz2",
 )
 
-new_git_repository(
-    name = "python_gflags_repo",
-    build_file = "@//debian:gflags.BUILD",
-    commit = "41c4571864f0db5823e07715317e7388e94faabc",
-    remote = "https://github.com/gflags/python-gflags.git",
-)
-
-bind(
-    name = "python-gflags",
-    actual = "@python_gflags_repo//:gflags",
-)
-
 local_repository(
     name = "com_github_gflags_gflags",
     path = "third_party/gflags",
 )
 
-# Downloaded from:
-# https://pypi.python.org/packages/source/g/glog/glog-0.1.tar.gz
-http_archive(
-    name = "python_glog_repo",
-    build_file = "@//debian:glog.BUILD",
-    sha256 = "953fd80122c48023d1148e6d1bda2763fcab59c8a81682bb298238a5935547b0",
-    strip_prefix = "glog-0.1",
-    url = "https://www.frc971.org/Build-Dependencies/glog-0.1.tar.gz",
-)
-
-bind(
-    name = "python-glog",
-    actual = "@python_glog_repo//:glog",
-)
-
 # Generated with:
 # git fetch https://github.com/wpilibsuite/ni-libraries main
 # git archive --output=allwpilib_ni-libraries_776db4e8aed31a651fa2f590e7468c69b384b42a.tar.gz --format=tar.gz 776db4e8aed31a651fa2f590e7468c69b384b42a
@@ -555,20 +519,10 @@
     url = "https://www.frc971.org/Build-Dependencies/allwpilib_ni-libraries_776db4e8aed31a651fa2f590e7468c69b384b42a.tar.gz",
 )
 
-# Downloaded from:
-# https://pypi.python.org/packages/source/s/six/six-1.10.0.tar.gz
-http_archive(
-    name = "six_repo",
-    build_file = "@//debian:six.BUILD",
-    sha256 = "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a",
-    strip_prefix = "six-1.10.0",
-    url = "https://www.frc971.org/Build-Dependencies/six-1.10.0.tar.gz",
-)
-
 # For protobuf. Don't use these.
 bind(
     name = "six",
-    actual = "@six_repo//:six",
+    actual = "@pip//six",
 )
 
 bind(
@@ -663,16 +617,6 @@
     url = "https://www.frc971.org/Build-Dependencies/mingw_compiler.tar.gz",
 )
 
-# Note that we should generally keep the matplotlib repo in a folder not
-# named matplotlib, because otherwise the repository itself tends to end up
-# on the PYTHONPATH, rather than the matplotlib folder within this repo.
-http_archive(
-    name = "matplotlib_repo",
-    build_file = "@//debian:matplotlib.BUILD",
-    sha256 = "71d1512f1a9a3c90496f0ef3adcd46c4e5e4da4310d7cbb6b0da01a07e5e76e8",
-    url = "https://www.frc971.org/Build-Dependencies/matplotlib-6.tar.gz",
-)
-
 http_archive(
     name = "patchelf",
     build_file = "@//debian:patchelf.BUILD",
@@ -688,13 +632,6 @@
 )
 
 http_archive(
-    name = "python_gtk",
-    build_file = "@//debian:python_gtk.BUILD",
-    sha256 = "36db18fc2b2c9012312b5d1cdc3d392d7e9756040f759ea50cb623fea29ae817",
-    url = "https://www.frc971.org/Build-Dependencies/python_gtk-4.tar.gz",
-)
-
-http_archive(
     name = "gtk_runtime",
     build_file = "@//debian:gtk_runtime.BUILD",
     sha256 = "5a6014d1783363be6bc95843d03bbb6513e650eaea60be2b1a4c65bf21981f9b",
@@ -714,13 +651,13 @@
 # Java11 JDK.
 remote_java_repository(
     name = "openjdk_linux_archive",
-    exec_compatible_with = [
-        "@platforms//cpu:x86_64",
-        "@platforms//os:linux",
-    ],
     prefix = "openjdk",
     sha256 = "60e65d32e38876f81ddb623e87ac26c820465b637e263e8bed1acdecb4ca9be2",
     strip_prefix = "zulu11.54.25-ca-jdk11.0.14.1-linux_x64",
+    target_compatible_with = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:linux",
+    ],
     urls = [
         "https://www.frc971.org/Build-Dependencies/zulu11.54.25-ca-jdk11.0.14.1-linux_x64.tar.gz",
     ],
@@ -729,13 +666,13 @@
 
 remote_java_repository(
     name = "openjdk_linux_archive_aarch64",
-    exec_compatible_with = [
-        "@platforms//cpu:aarch64",
-        "@platforms//os:linux",
-    ],
     prefix = "openjdk",
     sha256 = "b0fb0bc303bb05b5042ef3d0939b9489f4a49a13a2d1c8f03c5d8ab23099454d",
     strip_prefix = "zulu11.54.25-ca-jdk11.0.14.1-linux_aarch64",
+    target_compatible_with = [
+        "@platforms//cpu:aarch64",
+        "@platforms//os:linux",
+    ],
     urls = [
         "https://www.frc971.org/Build-Dependencies/zulu11.54.25-ca-jdk11.0.14.1-linux_aarch64.tar.gz",
     ],
@@ -988,65 +925,28 @@
     url = "https://www.frc971.org/Build-Dependencies/opencv_amd64_v3.tar.gz",
 )
 
-# Downloaded from:
-# https://github.com/halide/Halide/releases/download/release_2019_08_27/halide-linux-64-gcc53-800-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz
-# which is "Halide 2019/08/27" at https://github.com/halide/Halide/releases.
 http_archive(
     name = "halide_k8",
     build_file = "@//debian:halide.BUILD",
-    sha256 = "c67185d50a99adba86f6b2cc43c7e2cf11bcdfba9052d05e764a89b456a50446",
-    strip_prefix = "halide/",
-    url = "https://www.frc971.org/Build-Dependencies/halide-linux-64-gcc53-800-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz",
+    sha256 = "be3bdd067acb9ee0d37d0830821113cd69174bee46da466a836d8829fef7cf91",
+    strip_prefix = "Halide-14.0.0-x86-64-linux/",
+    url = "https://github.com/halide/Halide/releases/download/v14.0.0/Halide-14.0.0-x86-64-linux-6b9ed2afd1d6d0badf04986602c943e287d44e46.tar.gz",
 )
 
-# Downloaded from:
-# https://github.com/halide/Halide/releases/download/v8.0.0/halide-arm64-linux-64-trunk-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz
-# which is "Halide 8.0.0" at https://github.com/halide/Halide/releases.
-# The "2019/08/27" release was renamed as per the release notes:
-# https://github.com/halide/Halide/releases/tag/v8.0.0
 http_archive(
     name = "halide_arm64",
     build_file = "@//debian:halide.BUILD",
-    sha256 = "97b3e54565cd9df52abdd6452f3720ffd38861524154d74ae3d20dc949ed2a63",
-    strip_prefix = "halide/",
-    url = "https://www.frc971.org/Build-Dependencies/halide-arm64-linux-64-trunk-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz",
+    sha256 = "cdd42411bcbba682f73d7db0af69837c4857ee90f1727c6feb37fc9a98132385",
+    strip_prefix = "Halide-14.0.0-arm-64-linux/",
+    url = "https://github.com/halide/Halide/releases/download/v14.0.0/Halide-14.0.0-arm-64-linux-6b9ed2afd1d6d0badf04986602c943e287d44e46.tar.gz",
 )
 
-# Downloaded from:
-# https://github.com/halide/Halide/releases/download/release_2019_08_27/halide-arm32-linux-32-trunk-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz
-# which is "Halide 2019/08/27" at https://github.com/halide/Halide/releases.
 http_archive(
     name = "halide_armhf",
     build_file = "@//debian:halide.BUILD",
-    sha256 = "10564c559c9e04a173823413916d05fadd6e697d91bab21ddc5041190fa8f0f0",
-    strip_prefix = "halide/",
-    url = "https://www.frc971.org/Build-Dependencies/halide-arm32-linux-32-trunk-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz",
-)
-
-# Downloaded from:
-# https://files.pythonhosted.org/packages/0f/13/192104516c4a3d92dc6b5e106ffcfbf0fe35f3c4faa49650205ff652af72/opencv_python-4.5.1.48-cp37-cp37m-manylinux2014_x86_64.whl
-http_archive(
-    name = "opencv_contrib_nonfree_amd64",
-    build_file = "@//debian:opencv_python.BUILD",
-    sha256 = "a1dfa0486db367594510c0c799ec7481247dc86e651b69008806d875ab731471",
-    type = "zip",
-    url = "https://www.frc971.org/Build-Dependencies/opencv_python-4.5.1.48-cp39-cp39-manylinux2014_x86_64.whl",
-)
-
-http_archive(
-    name = "osqp_amd64",
-    build_file = "@//debian:osqp_python.BUILD",
-    sha256 = "8003fc363f707daa46fef3af548e6a580372154d6cd49a7bf2f569ba5f807d15",
-    type = "zip",
-    url = "https://files.pythonhosted.org/packages/3f/e2/f1c40e890f00f8a566bc2481d0f215e52def3dfe8eea6b8ad4cc2d3cbca2/osqp-0.6.2.post5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
-)
-
-http_archive(
-    name = "qdldl_amd64",
-    build_file = "@//debian:qdldl_python.BUILD",
-    sha256 = "2c09f4b1a1c6f3a0579af004443417e084491e7c844ff9fb73170bb5d43f70b5",
-    type = "zip",
-    url = "https://files.pythonhosted.org/packages/9e/26/ccb4f065b40c1e9ff35ee66970d4fa97dd2fe221b846da2110eb8cd6c3f4/qdldl-0.1.5.post0-cp39-cp39-manylinux2014_x86_64.whl",
+    sha256 = "6b3fe3396391b57990a2c41d8dcea74b0734d1b2a0fd42fe0858d954aa45df2b",
+    strip_prefix = "Halide-14.0.0-arm-32-linux/",
+    url = "https://github.com/halide/Halide/releases/download/v14.0.0/Halide-14.0.0-arm-32-linux-6b9ed2afd1d6d0badf04986602c943e287d44e46.tar.gz",
 )
 
 http_archive(
@@ -1070,34 +970,6 @@
     url = "https://www.frc971.org/Build-Dependencies/gstreamer_1.20.1-1~bpo11+1_arm64.tar.gz",
 )
 
-# Downloaded from:
-# https://files.pythonhosted.org/packages/64/a7/45e11eebf2f15bf987c3bc11d37dcc838d9dc81250e67e4c5968f6008b6c/Jinja2-2.11.2.tar.gz
-http_archive(
-    name = "python_jinja2",
-    build_file = "@//debian:python_jinja2.BUILD",
-    sha256 = "89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
-    strip_prefix = "Jinja2-2.11.2",
-    url = "https://www.frc971.org/Build-Dependencies/Jinja2-2.11.2.tar.gz",
-)
-
-# Downloaded from:
-# https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz
-http_archive(
-    name = "python_markupsafe",
-    build_file = "@//debian:python_markupsafe.BUILD",
-    sha256 = "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
-    strip_prefix = "MarkupSafe-1.1.1",
-    url = "https://www.frc971.org/Build-Dependencies/MarkupSafe-1.1.1.tar.gz",
-)
-
-http_archive(
-    name = "python_yapf",
-    build_file = "@//debian:python_yapf.BUILD",
-    sha256 = "410ed0f592c898d75d73f7792aee6569bdbc0b57bc72b417c722c17f41f66b12",
-    strip_prefix = "yapf-0.32.0",
-    url = "https://github.com/google/yapf/archive/refs/tags/v0.32.0.tar.gz",
-)
-
 # //debian:lzma_amd64
 http_archive(
     name = "lzma_amd64",
@@ -1196,23 +1068,6 @@
 )
 
 http_archive(
-    name = "rules_pkg",
-    patch_args = ["-p1"],
-    patches = [
-        "//third_party:rules_pkg/0001-Fix-tree-artifacts.patch",
-    ],
-    sha256 = "62eeb544ff1ef41d786e329e1536c1d541bb9bcad27ae984d57f18f314018e66",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
-        "https://github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
-    ],
-)
-
-load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
-
-rules_pkg_dependencies()
-
-http_archive(
     name = "libtinfo5_amd64",
     build_file_content = """
 exports_files(
diff --git a/aos/starter/BUILD b/aos/starter/BUILD
index d4566a0..5690bda 100644
--- a/aos/starter/BUILD
+++ b/aos/starter/BUILD
@@ -172,6 +172,14 @@
 )
 
 flatbuffer_cc_library(
+    name = "kthread_fbs",
+    srcs = ["kthread.fbs"],
+    gen_reflections = True,
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+)
+
+flatbuffer_cc_library(
     name = "starter_rpc_fbs",
     srcs = ["starter_rpc.fbs"],
     gen_reflections = True,
@@ -201,3 +209,39 @@
         "//aos/events/logging:logger_main",
     ],
 )
+
+cc_library(
+    name = "irq_affinity_lib",
+    srcs = ["irq_affinity_lib.cc"],
+    hdrs = ["irq_affinity_lib.h"],
+    deps = [
+        "//aos/scoped:scoped_fd",
+        "@com_github_google_glog//:glog",
+        "@com_google_absl//absl/strings",
+    ],
+)
+
+cc_binary(
+    name = "irq_affinity",
+    srcs = [
+        "irq_affinity.cc",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":irq_affinity_lib",
+        ":kthread_fbs",
+        "//aos:init",
+        "//aos:realtime",
+        "//aos/events:shm_event_loop",
+        "//aos/util:top",
+    ],
+)
+
+cc_test(
+    name = "irq_affinity_lib_test",
+    srcs = ["irq_affinity_lib_test.cc"],
+    deps = [
+        ":irq_affinity_lib",
+        "//aos/testing:googletest",
+    ],
+)
diff --git a/aos/starter/irq_affinity.cc b/aos/starter/irq_affinity.cc
new file mode 100644
index 0000000..3f32ec9
--- /dev/null
+++ b/aos/starter/irq_affinity.cc
@@ -0,0 +1,387 @@
+#include <linux/securebits.h>
+#include <pwd.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "aos/starter/irq_affinity_lib.h"
+#include "aos/starter/kthread_generated.h"
+#include "aos/util/top.h"
+#include "gflags/gflags.h"
+
+DEFINE_string(config, "aos_config.json", "File path of aos configuration");
+
+DEFINE_string(user, "",
+              "Starter runs as though this user ran a SUID binary if set.");
+
+namespace aos {
+
+cpu_set_t AffinityFromFlatbuffer(const flatbuffers::Vector<uint8_t> *v) {
+  cpu_set_t affinity;
+  CPU_ZERO(&affinity);
+  if (v == nullptr) {
+    for (int i = 0; i < CPU_SETSIZE; ++i) {
+      CPU_SET(i, &affinity);
+    }
+  } else {
+    for (uint8_t cpu : *v) {
+      CPU_SET(cpu, &affinity);
+    }
+  }
+  return affinity;
+}
+
+// Class to hold the configuration for an IRQ.
+struct ParsedIrqConfig {
+  std::string name;
+  cpu_set_t affinity;
+
+  void ConfigureIrq(int interrupt_number) const {
+    const std::string affinity_filename =
+        absl::StrCat("/proc/irq/", interrupt_number, "/smp_affinity");
+    const std::string contents = util::ReadFileToStringOrDie(affinity_filename);
+
+    std::string new_contents = std::string(contents.size() - 1, '0');
+
+    // Contents will be a padded string which is the size of the number of
+    // IRQs.
+    CHECK(!(CPU_SETSIZE & 0xf));
+    for (size_t i = 0; i < CPU_SETSIZE; i += 4) {
+      if (i / 4 >= new_contents.size()) {
+        break;
+      }
+      uint8_t byte = 0;
+      if (CPU_ISSET(i + 0, &affinity)) {
+        byte |= 1;
+      }
+      if (CPU_ISSET(i + 1, &affinity)) {
+        byte |= 2;
+      }
+      if (CPU_ISSET(i + 2, &affinity)) {
+        byte |= 4;
+      }
+      if (CPU_ISSET(i + 3, &affinity)) {
+        byte |= 8;
+      }
+      if (byte < 10) {
+        new_contents[new_contents.size() - 1 - i / 4] = '0' + byte;
+      } else {
+        new_contents[new_contents.size() - 1 - i / 4] = 'a' + (byte - 10);
+      }
+    }
+
+    if (contents != new_contents) {
+      util::WriteStringToFileOrDie(affinity_filename, new_contents);
+    }
+  }
+};
+
+// Class to hold the configuration for a kthread.
+struct ParsedKThreadConfig {
+  bool full_match = false;
+  std::string prefix;
+  std::string postfix;
+  starter::Scheduler scheduler;
+  int priority;
+  cpu_set_t affinity;
+
+  bool Matches(std::string_view candidate) const {
+    if (full_match) {
+      return candidate == prefix;
+    } else {
+      if (candidate.size() < prefix.size() + postfix.size()) {
+        return false;
+      }
+      if (candidate.substr(0, prefix.size()) != prefix) {
+        return false;
+      }
+      if (candidate.substr(candidate.size() - postfix.size(), postfix.size()) !=
+          postfix) {
+        return false;
+      }
+      return true;
+    }
+  }
+
+  void ConfigurePid(pid_t pid) const {
+    {
+      struct sched_param param;
+      param.sched_priority = priority;
+      int new_scheduler;
+      switch (scheduler) {
+        case starter::Scheduler::SCHEDULER_OTHER:
+          new_scheduler = SCHED_OTHER;
+          break;
+        case starter::Scheduler::SCHEDULER_RR:
+          new_scheduler = SCHED_RR;
+          break;
+        case starter::Scheduler::SCHEDULER_FIFO:
+          new_scheduler = SCHED_FIFO;
+          break;
+        default:
+          LOG(FATAL) << "Unknown scheduler";
+      }
+      PCHECK(sched_setscheduler(pid, new_scheduler, &param) == 0);
+    }
+    PCHECK(sched_setaffinity(pid, sizeof(affinity), &affinity) == 0);
+  }
+};
+
+// TODO(austin): Clean this up a bit, and maybe we can add some tests.
+class IrqAffinity {
+ public:
+  IrqAffinity(
+      EventLoop *event_loop,
+      const aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
+          &irq_affinity_config)
+      : top_(event_loop) {
+    if (irq_affinity_config.message().has_kthreads()) {
+      kthreads_.reserve(irq_affinity_config.message().kthreads()->size());
+      for (const starter::KthreadConfig *kthread_config :
+           *irq_affinity_config.message().kthreads()) {
+        LOG(INFO) << "Kthread " << aos::FlatbufferToJson(kthread_config);
+        CHECK(kthread_config->has_name()) << ": Name required";
+        const size_t star_position =
+            kthread_config->name()->string_view().find('*');
+        const bool has_star = star_position != std::string_view::npos;
+
+        kthreads_.push_back(ParsedKThreadConfig{
+            .full_match = !has_star,
+            .prefix = std::string(
+                !has_star ? kthread_config->name()->string_view()
+                          : kthread_config->name()->string_view().substr(
+                                0, star_position)),
+            .postfix = std::string(
+                !has_star ? ""
+                          : kthread_config->name()->string_view().substr(
+                                star_position + 1)),
+            .scheduler = kthread_config->scheduler(),
+            .priority = kthread_config->priority(),
+            .affinity = AffinityFromFlatbuffer(kthread_config->affinity()),
+        });
+      }
+    }
+
+    if (irq_affinity_config.message().has_irqs()) {
+      irqs_.reserve(irq_affinity_config.message().irqs()->size());
+      for (const starter::IrqConfig *irq_config :
+           *irq_affinity_config.message().irqs()) {
+        CHECK(irq_config->has_name()) << ": Name required";
+        LOG(INFO) << "IRQ " << aos::FlatbufferToJson(irq_config);
+        irqs_.push_back(ParsedIrqConfig{
+            .name = irq_config->name()->str(),
+            .affinity = AffinityFromFlatbuffer(irq_config->affinity()),
+        });
+      }
+    }
+
+    top_.set_track_top_processes(true);
+    top_.set_on_reading_update([this]() {
+      for (const std::pair<const pid_t, util::Top::ProcessReadings> &reading :
+           top_.readings()) {
+        if (reading.second.kthread) {
+          for (const ParsedKThreadConfig &match : kthreads_) {
+            if (match.Matches(reading.second.name)) {
+              match.ConfigurePid(reading.first);
+              break;
+            }
+          }
+        }
+      }
+
+      interrupts_status_.Update();
+
+      for (const InterruptsStatus::InterruptState &state :
+           interrupts_status_.states()) {
+        for (const ParsedIrqConfig &match : irqs_) {
+          bool matched = false;
+          for (const std::string &action : state.actions) {
+            if (match.name == action) {
+              matched = true;
+              break;
+            }
+          }
+          if (matched) {
+            match.ConfigureIrq(state.interrupt_number);
+          }
+        }
+      }
+    });
+  }
+
+ private:
+  util::Top top_;
+
+  // TODO(austin): Publish message with everything in it.
+  // TODO(austin): Make Top report out affinity + priority + scheduler for
+  // posterity.
+
+  std::vector<ParsedKThreadConfig> kthreads_;
+  std::vector<ParsedIrqConfig> irqs_;
+
+  InterruptsStatus interrupts_status_;
+};
+
+}  // namespace aos
+
+int main(int argc, char **argv) {
+  aos::InitGoogle(&argc, &argv);
+
+  if (!FLAGS_user.empty()) {
+    // Maintain root permissions as we switch to become the user so we can
+    // actually manipulate priorities.
+    PCHECK(prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NOROOT) ==
+           0);
+
+    uid_t uid;
+    uid_t gid;
+    {
+      struct passwd *user_data = getpwnam(FLAGS_user.c_str());
+      if (user_data != nullptr) {
+        uid = user_data->pw_uid;
+        gid = user_data->pw_gid;
+      } else {
+        LOG(FATAL) << "Could not find user " << FLAGS_user;
+        return 1;
+      }
+    }
+    // Change the real and effective IDs to the user we're running as. The
+    // effective IDs mean files we access (like shared memory) will happen as
+    // that user. The real IDs allow child processes with an different effective
+    // ID to still participate in signal sending/receiving.
+    constexpr int kUnchanged = -1;
+    if (setresgid(/* ruid */ gid, /* euid */ gid,
+                  /* suid */ kUnchanged) != 0) {
+      PLOG(FATAL) << "Failed to change GID to " << FLAGS_user;
+    }
+
+    if (setresuid(/* ruid */ uid, /* euid */ uid,
+                  /* suid */ kUnchanged) != 0) {
+      PLOG(FATAL) << "Failed to change UID to " << FLAGS_user;
+    }
+  }
+
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(FLAGS_config);
+
+  // TODO(austin): File instead of hard-coded JSON.
+  aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
+      irq_affinity_config =
+          aos::JsonToFlatbuffer<aos::starter::IrqAffinityConfig>(
+              R"json({
+  "irqs": [
+    {
+      "name": "ttyS2",
+      "affinity": [1]
+    },
+    {
+      "name": "dw-mci",
+      "affinity": [1]
+    },
+    {
+      "name": "mmc1",
+      "affinity": [1]
+    },
+    {
+      "name": "rkisp1",
+      "affinity": [2]
+    },
+    {
+      "name": "ff3c0000.i2c",
+      "affinity": [2]
+    },
+    {
+      "name": "ff3d0000.i2c",
+      "affinity": [2]
+    },
+    {
+      "name": "ff6e0000.dma-controller",
+      "affinity": [0]
+    },
+    {
+      "name": "ff1d0000.spi",
+      "affinity": [0]
+    },
+    {
+      "name": "eth0",
+      "affinity": [1]
+    }
+  ],
+  "kthreads": [
+    {
+      "name": "irq/*-ff940000.hdmi",
+      "scheduler": "SCHEDULER_OTHER"
+    },
+    {
+      "name": "irq/*-rockchip_usb2phy",
+      "scheduler": "SCHEDULER_OTHER"
+    },
+    {
+      "name": "irq/*-mmc0",
+      "scheduler": "SCHEDULER_OTHER"
+    },
+    {
+      "name": "irq/*-mmc1",
+      "scheduler": "SCHEDULER_OTHER"
+    },
+    {
+      "name": "irq/*-fe320000.mmc cd",
+      "scheduler": "SCHEDULER_OTHER"
+    },
+    {
+      "name": "irq/*-vc4 crtc",
+      "scheduler": "SCHEDULER_OTHER"
+    },
+    {
+      "name": "irq/*-rkisp1",
+      "scheduler": "SCHEDULER_FIFO",
+      "priority": 60,
+      "affinity": [2]
+    },
+    {
+      "name": "irq/*-ff3c0000.i2c",
+      "scheduler": "SCHEDULER_FIFO",
+      "priority": 51,
+      "affinity": [2]
+    },
+    {
+      "name": "irq/*-adis16505",
+      "scheduler": "SCHEDULER_FIFO",
+      "priority": 59,
+      "affinity": [0]
+    },
+    {
+      "name": "irq/*-ff6e0000.dma-controller",
+      "scheduler": "SCHEDULER_FIFO",
+      "priority": 59,
+      "affinity": [0]
+    },
+    {
+      "name": "spi0",
+      "scheduler": "SCHEDULER_FIFO",
+      "priority": 57,
+      "affinity": [0]
+    },
+    {
+      "name": "irq/*-eth0",
+      "scheduler": "SCHEDULER_FIFO",
+      "priority": 10,
+      "affinity": [1]
+    },
+    {
+      "name": "irq/*-rockchip_thermal",
+      "scheduler": "SCHEDULER_FIFO",
+      "priority": 1
+    }
+  ]
+})json");
+
+  aos::ShmEventLoop shm_event_loop(&config.message());
+
+  aos::IrqAffinity irq_affinity(&shm_event_loop, irq_affinity_config);
+
+  shm_event_loop.Run();
+
+  return 0;
+}
diff --git a/aos/starter/irq_affinity_lib.cc b/aos/starter/irq_affinity_lib.cc
new file mode 100644
index 0000000..ccc4853
--- /dev/null
+++ b/aos/starter/irq_affinity_lib.cc
@@ -0,0 +1,288 @@
+#include "aos/starter/irq_affinity_lib.h"
+
+#include <fcntl.h>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_split.h"
+#include "aos/scoped/scoped_fd.h"
+
+namespace aos {
+
+// Class to split strings by whitespace with absl::StrSplit.
+class ByWhitespace {
+ public:
+  // Returns the location of the next separator per the StrSplit API.
+  absl::string_view Find(absl::string_view text, size_t pos) const {
+    size_t count = 0;
+    const char *start = text.data() + text.size();
+    for (size_t i = pos; i < text.size(); ++i) {
+      if (text[i] == ' ') {
+        if (count == 0) {
+          start = text.data() + i;
+        }
+        ++count;
+      } else {
+        if (count > 0) {
+          break;
+        }
+      }
+    }
+    return std::string_view(start, count);
+  }
+};
+
+InterruptsStatus::InterruptsStatus() {
+  // 8k seems to be big enough to hold most things, so let's start there.
+  interrupts_content_.reserve(8192);
+}
+
+namespace {
+
+// Counts the number of "CPU" strings in the line without allocating.
+size_t CountCores(std::string_view line) {
+  size_t cpus_found = 0;
+  std::string_view::size_type start_pos = 0;
+  while (std::string_view::npos != (start_pos = line.find("CPU", start_pos))) {
+    start_pos += 3;
+    ++cpus_found;
+  }
+  return cpus_found;
+}
+
+}  // namespace
+
+void InterruptsStatus::Update(std::string_view contents) {
+  size_t line_number = 0;
+  for (std::string_view line :
+       absl::StrSplit(contents, "\n", absl::SkipEmpty())) {
+    if (line_number == 0) {
+      // This is the CPUs line.  Count our cores.
+      const size_t cpus_found = CountCores(line);
+
+      if (cpus_ == 0) {
+        // First time through.
+        cpus_ = cpus_found;
+      } else {
+        CHECK_EQ(cpus_found, cpus_) << ": Number of CPUs changed while running";
+      }
+    } else {
+      size_t element_number = 0;
+      InterruptState *state = nullptr;
+      bool new_element = false;
+      for (const std::string_view element :
+           absl::StrSplit(absl::StripAsciiWhitespace(line),
+                          absl::MaxSplits(ByWhitespace(), cpus_ + 1))) {
+        if (element_number == 0) {
+          // Parse the interrupt number.  This should either be in the form of:
+          //  23:
+          // or
+          //  Err:
+          CHECK_EQ(element[element.size() - 1], ':')
+              << ": Missing trailing ':'";
+
+          int interrupt_number;
+          std::string_view interrupt_name =
+              element.substr(0, element.size() - 1);
+
+          // It's a named interrupt.
+          if (!absl::SimpleAtoi(interrupt_name, &interrupt_number)) {
+            interrupt_number = -1;
+          } else {
+            interrupt_name = "";
+          }
+
+          // Add a new element if we are too short, or if the interrupt changed.
+          if (states_.size() < line_number) {
+            new_element = true;
+          } else {
+            InterruptState *state = &states_[line_number - 1];
+            if (state->interrupt_number != interrupt_number ||
+                state->interrupt_name != interrupt_name) {
+              VLOG(1)
+                  << "IRQ changed names...  Blow away the end and try again.";
+              // This happens infrequently enough that it isn't worth trying to
+              // resize things.  It may never happen while running.  Nuke
+              // anything missing and retry.
+              states_.resize(line_number - 1);
+              new_element = true;
+            }
+          }
+
+          if (new_element) {
+            InterruptState new_state;
+            std::vector<unsigned int> irq_count(cpus_, 0);
+            states_.push_back(InterruptState{
+                .interrupt_number = interrupt_number,
+                .interrupt_name = interrupt_number == -1
+                                      ? std::string(interrupt_name)
+                                      : std::string(),
+                .count = std::move(irq_count),
+                .chip_name = std::string(),
+                .description = std::string(),
+                .hwirq = std::string(),
+                .actions = {},
+            });
+          }
+          state = &states_[line_number - 1];
+
+        } else if (element_number <= cpus_) {
+          // We are now parsing the count body.  Keep updating the elements.
+          unsigned int interrupt_count;
+          CHECK(absl::SimpleAtoi(element, &interrupt_count))
+              << ": Failed to parse count " << interrupt_count;
+          state->count[element_number - 1] = interrupt_count;
+        } else if (element_number == cpus_ + 1) {
+          if (state->interrupt_number == -1) {
+            // Named interrupt, the rest of the string is the description.
+            if (new_element) {
+              state->description = std::string(element);
+            } else {
+              CHECK_EQ(state->description, element) << ": Description changed";
+            }
+          } else {
+            // Ok, the rest is now some properties of the interrupt.
+            size_t trailing_elements_count = 0;
+            for (std::string_view trailing_element :
+                 absl::StrSplit(absl::StripAsciiWhitespace(element),
+                                absl::MaxSplits(ByWhitespace(), 2))) {
+              if (trailing_elements_count == 0) {
+                // Chip name.
+                if (new_element) {
+                  state->chip_name = std::string(trailing_element);
+                } else {
+                  CHECK_EQ(state->chip_name, trailing_element)
+                      << ": Chip changed names";
+                }
+              } else if (trailing_elements_count == 1) {
+                // Hardware IRQ
+                if (new_element) {
+                  state->hwirq = std::string(trailing_element);
+                } else {
+                  CHECK_EQ(state->hwirq, trailing_element)
+                      << ": Hardware IRQ changed names";
+                }
+              } else {
+                // And then either "Level/Edge" and then the actions, or just
+                // the actions. Kernel has CONFIG_GENERIC_IRQ_SHOW_LEVEL
+                // enabled if the string starts with either..  Strip it until someone finds a use.
+                if (absl::StartsWith(trailing_element, "Level")) {
+                  trailing_element =
+                      absl::StripAsciiWhitespace(trailing_element.substr(5));
+                } else if (absl::StartsWith(trailing_element, "Edge")) {
+                  trailing_element =
+                      absl::StripAsciiWhitespace(trailing_element.substr(4));
+                }
+
+                // Split up the actions by ", " and stick them in.
+                if (new_element) {
+                  state->actions = std::vector<std::string>(
+                      absl::StrSplit(trailing_element, ", "));
+                } else {
+                  size_t action_index = 0;
+                  bool matches = true;
+                  // Start by comparing.  If we don't match, then set.  This
+                  // avoids an allocation if we can get away with it.
+                  for (std::string_view action :
+                       absl::StrSplit(trailing_element, ", ")) {
+                    if (action_index >= state->actions.size()) {
+                      matches = false;
+                      break;
+                    }
+                    if (state->actions[action_index] != action) {
+                      matches = false;
+                      break;
+                    }
+                    ++action_index;
+                  }
+                  if (!matches) {
+                    state->actions = std::vector<std::string>(
+                        absl::StrSplit(trailing_element, ", "));
+                  }
+                }
+              }
+              ++trailing_elements_count;
+            }
+          }
+        } else {
+          LOG(FATAL) << "Unexpected element, need to consume " << element;
+        }
+        ++element_number;
+      }
+
+      // Validate that everything makes sense and we have the elements expected.
+      if (state->interrupt_number != -1) {
+        CHECK_EQ(element_number, cpus_ + 2);
+      } else {
+        // Only these 3 interrupts are known to not be per core.
+        if (state->interrupt_name == "Err" || state->interrupt_name == "ERR" ||
+            state->interrupt_name == "MIS") {
+          if (new_element) {
+            CHECK_LE(element_number, cpus_ + 1);
+            state->count.resize(element_number - 1);
+          } else {
+            CHECK_EQ(state->count.size(), element_number - 1);
+          }
+        } else {
+          CHECK_EQ(element_number, cpus_ + 2);
+        }
+      }
+
+      if (VLOG_IS_ON(1)) {
+        if (state->interrupt_number == -1) {
+          LOG(INFO) << "IRQ: " << state->interrupt_name;
+        } else {
+          LOG(INFO) << "IRQ: " << state->interrupt_number;
+        }
+        for (unsigned int c : state->count) {
+          LOG(INFO) << "  " << c;
+        }
+        if (!state->chip_name.empty()) {
+          LOG(INFO) << "chip_name \"" << state->chip_name << "\"";
+        }
+        if (!state->description.empty()) {
+          LOG(INFO) << "description \"" << state->description << "\"";
+        }
+        if (!state->hwirq.empty()) {
+          LOG(INFO) << "hwirq \"" << state->hwirq << "\"";
+        }
+        if (!state->actions.empty()) {
+          for (const std::string &action : state->actions) {
+            LOG(INFO) << "  action \"" << action << "\"";
+          }
+        }
+      }
+    }
+
+    ++line_number;
+  }
+}
+
+void InterruptsStatus::Update() {
+  ScopedFD fd(open("/proc/interrupts", O_RDONLY));
+  size_t so_far = 0;
+  while (true) {
+    // Keep growing the size of interrupts_content_ until it holds the whole
+    // string.
+    size_t kStride = 8192;
+    if (interrupts_content_.capacity() < so_far + kStride) {
+      interrupts_content_.reserve(interrupts_content_.capacity() + kStride);
+    }
+
+    interrupts_content_.resize(interrupts_content_.capacity());
+
+    const ssize_t result = read(fd.get(), interrupts_content_.data() + so_far,
+                                interrupts_content_.capacity() - so_far);
+    PCHECK(result >= 0) << ": reading from /proc/interrupts";
+    if (result == 0) {
+      break;
+    }
+    so_far += result;
+  }
+
+  interrupts_content_.resize(so_far);
+  Update(
+      std::string_view(interrupts_content_.data(), interrupts_content_.size()));
+}
+
+}  // namespace aos
diff --git a/aos/starter/irq_affinity_lib.h b/aos/starter/irq_affinity_lib.h
new file mode 100644
index 0000000..ccb7d03
--- /dev/null
+++ b/aos/starter/irq_affinity_lib.h
@@ -0,0 +1,63 @@
+#ifndef AOS_STARTER_IRQ_AFFINITY_LIB_H_
+#define AOS_STARTER_IRQ_AFFINITY_LIB_H_
+
+#include <string>
+#include <vector>
+
+#include "glog/logging.h"
+
+namespace aos {
+
+// Class to parse /proc/interrupts.
+class InterruptsStatus {
+ public:
+  InterruptsStatus();
+
+  // Updates the interrupt state.
+  void Update();
+
+  // Updates the interrupt state from the contents of /proc/interrupts.
+  //
+  // This should only be used for testing.
+  void Update(std::string_view contents);
+
+  // Information about each interrupt.
+  struct InterruptState {
+    // IRQ number.  -1 if this doesn't have a number.
+    int interrupt_number;
+    // Name of the interrupt.  Only populated when number == -1
+    std::string interrupt_name;
+    // IRQs triggered per core, where the vector index is the core.
+    std::vector<unsigned int> count;
+
+    // The name of the irq chip controller.
+    std::string chip_name;
+
+    // Description of the IRQ if it doesn't have an interrupt number.
+    std::string description;
+
+    // Hardware IRQ "number".
+    std::string hwirq;
+
+    // List of actions.  An action is something which gets triggered on an
+    // interrupt.  This is the IRQ "name", and is a vector to cover shared IRQs.
+    std::vector<std::string> actions;
+  };
+
+  // Information about all IRQs.
+  const std::vector<InterruptState> &states() const { return states_; }
+
+ private:
+
+  // Buffer to hold the contents of /proc/interrupts to avoid re-allocating
+  // continually.
+  std::vector<char> interrupts_content_;
+
+  size_t cpus_ = 0;
+
+  std::vector<InterruptState> states_;
+};
+
+}  // namespace aos
+
+#endif  // AOS_STARTER_IRQ_AFFINITY_LIB_H_
diff --git a/aos/starter/irq_affinity_lib_test.cc b/aos/starter/irq_affinity_lib_test.cc
new file mode 100644
index 0000000..0fe240b
--- /dev/null
+++ b/aos/starter/irq_affinity_lib_test.cc
@@ -0,0 +1,278 @@
+#include "aos/starter/irq_affinity_lib.h"
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+namespace aos::testing {
+
+constexpr std::string_view kRockPiContents =
+    R"contents(           CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       
+ 23:    4221703    2325251    5737616    1332002    1418781    1368697     GICv3  30 Level     arch_timer
+ 25:    8034134    3985740    5155495    1708244    1835948    1635570     GICv3 113 Level     rk_timer
+ 31:      11137       2335       4349        978          0          0  GICv3-23   0 Level     arm-pmu
+ 32:          0          0          0          0        837        820  GICv3-23   1 Level     arm-pmu
+ 33:          0          0          0          0          0          0     GICv3  59 Level     rockchip_usb2phy
+ 34:          0          0          0          0          0          0     GICv3  63 Level     rockchip_usb2phy
+ 35:          0          0          0          0          0          0     GICv3  37 Level     ff6d0000.dma-controller
+ 36:          0          0          0          0          0          0     GICv3  38 Level     ff6d0000.dma-controller
+ 37:          0          0          0          0          0          0     GICv3  39 Level     ff6e0000.dma-controller
+ 38:   97326477          0          0          0          0          0     GICv3  40 Level     ff6e0000.dma-controller
+ 39:        240          0          0          0          0          0     GICv3 132 Level     ttyS2
+ 40:          0          0          0          0          0          0     GICv3 147 Level     ff650800.iommu
+ 41:          0          0          0          0          0          0     GICv3 149 Level     ff660480.iommu
+ 42:          0          0          0          0          0          0     GICv3 151 Level     ff8f3f00.iommu, ff8f0000.vop
+ 43:          0          0          0          0          0          0     GICv3 150 Level     ff903f00.iommu, ff900000.vop
+ 44:          0          0    3412401          0          0          0     GICv3  75 Level     ff914000.iommu, rkisp1
+ 45:          0          0          0          0          0          0     GICv3  76 Level     ff924000.iommu
+ 46:          0          0          0          0          0          0     GICv3  85 Level     ff1d0000.spi
+ 47:          0          0          0          0          0          0     GICv3  91 Level     ff110000.i2c
+ 48:          0          0          0          0          0          0     GICv3  66 Level     ff130000.i2c
+ 49:        264          0       1386          0          0          0     GICv3  89 Level     ff3c0000.i2c
+ 50:          0          0          0          0          0          0  rockchip_gpio_irq  21 Level     rk808
+ 56:          0          0          0          0          0          0     rk808   5 Edge      RTC alarm
+ 60:         54          0        357          0          0          0     GICv3  88 Level     ff3d0000.i2c
+ 61:          0          0          0          0          0          0     GICv3 152 Edge      ff848000.watchdog
+ 62:       9491      70670          0          0          0          0     GICv3  97 Level     dw-mci
+ 63:        207          0          0          0          0          0     GICv3  43 Level     mmc1
+ 64:          0          0          0          0          0          0  rockchip_gpio_irq   7 Edge      fe320000.mmc cd
+ 65:          0          0          0          0          0          0     GICv3 129 Level     rockchip_thermal
+ 66:          0          0          0          0          0          0     GICv3  94 Level     ff100000.saradc
+ 67:          0          0          0          0          0          0     GICv3  58 Level     ehci_hcd:usb1
+ 68:          0          0          0          0          0          0     GICv3  62 Level     ehci_hcd:usb2
+ 69:          0          0          0          0          0          0     GICv3  60 Level     ohci_hcd:usb3
+ 70:          0          0          0          0          0          0     GICv3  64 Level     ohci_hcd:usb4
+ 71:         37          0          0      33541          0          0     GICv3  44 Level     eth0
+ 72:          0          0          0          0          0          0     GICv3 137 Level     xhci-hcd:usb5
+ 73:      52061          0          0          0          0          0     GICv3 142 Level     xhci-hcd:usb7
+ 74:          0          0          0          0          0          0     GICv3  87 Level     ff680000.rga
+ 75:          0          0          0          0          0          0     GICv3 148 Level     ff660000.video-codec
+ 76:          0          0          0          0          0          0     GICv3 146 Level     ff650000.video-codec
+ 77:          0          0          0          0          0          0     GICv3 145 Level     ff650000.video-codec
+ 78:          0          0          0          0          0          0     GICv3  55 Level     ff940000.hdmi
+ 79:   50468710          0          0          0          0          0  rockchip_gpio_irq  16 Level     adis16505
+IPI0:    629986   29367897    1604808    9830343    7634766    7528717       Rescheduling interrupts
+IPI1:     11040      79148      22290      41912      29091      21995       Function call interrupts
+IPI2:         0          0          0          0          0          0       CPU stop interrupts
+IPI3:         0          0          0          0          0          0       CPU stop (for crash dump) interrupts
+IPI4:   5391338    7612504    8510753   10479370   10766273   10712987       Timer broadcast interrupts
+IPI5:   3040632     140996    4922363      50728      31487      24500       IRQ work interrupts
+IPI6:         0          0          0          0          0          0       CPU wake-up interrupts
+Err:          0
+)contents";
+
+constexpr std::string_view kRockPiContents2 =
+    R"contents(           CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       
+ 23:       1703      25251     737616       2002      18781     368697     GICv3  30 Level     arch_timer
+ 25:    8034134    3985740    5155495    1708244    1835948    1635570     GICv3 113 Level     rk_timer
+ 31:      11137       2335       4349        978          0          0  GICv3-23   0 Level     arm-pmu
+ 32:          0          0          0          0        837        820  GICv3-23   1 Level     arm-pmu
+ 33:          0          0          0          0          0          0     GICv3  59 Level     rockchip_usb2phy
+ 34:          0          0          0          0          0          0     GICv3  63 Level     rockchip_usb2phy
+ 35:          0          0          0          0          0          0     GICv3  37 Level     ff6d0000.dma-controller
+ 36:          0          0          0          0          0          0     GICv3  38 Level     ff6d0000.dma-controller
+ 37:          0          0          0          0          0          0     GICv3  39 Level     ff6e0000.dma-controller
+ 38:   97326477          0          0          0          0          0     GICv3  40 Level     ff6e0000.dma-controller
+ 39:        240          0          0          0          0          0     GICv3 132 Level     ttyS2
+ 40:          0          0          0          0          0          0     GICv3 147 Level     ff650800.iommu
+ 41:          0          0          0          0          0          0     GICv3 149 Level     ff660480.iommu
+ 42:          0          0          0          0          0          0     GICv3 151 Level     ff8f3f00.iommu, ff8f0000.vop
+ 43:          0          0          0          0          0          0     GICv3 150 Level     ff903f00.iommu, ff900000.vop
+ 44:          0          0    3412401          0          0          0     GICv3  75 Level     ff914000.iommu, rkisp1
+ 45:          0          0          0          0          0          0     GICv3  76 Level     ff924000.iommu
+ 46:          0          0          0          0          0          0     GICv3  85 Level     ff1d0000.spi
+ 47:          0          0          0          0          0          0     GICv3  91 Level     ff110000.i2c
+ 48:          0          0          0          0          0          0     GICv3  66 Level     ff130000.i2c
+ 49:        264          0       1386          0          0          0     GICv3  89 Level     ff3c0000.i2c
+ 50:          0          0          0          0          0          0  rockchip_gpio_irq  21 Level     rk808
+ 56:          0          0          0          0          0          0     rk808   5 Edge      RTC alarm
+ 60:         54          0        357          0          0          0     GICv3  88 Level     ff3d0000.i2c
+ 61:          0          0          0          0          0          0     GICv3 152 Edge      ff848000.watchdog
+ 62:       9491      70670          0          0          0          0     GICv3  97 Level     dw-mci
+ 63:        207          0          0          0          0          0     GICv3  43 Level     mmc1
+ 64:          0          0          0          0          0          0  rockchip_gpio_irq   7 Edge      fe320000.mmc cd
+ 65:          0          0          0          0          0          0     GICv3 129 Level     rockchip_thermal
+ 66:          0          0          0          0          0          0     GICv3  94 Level     ff100000.saradc
+ 67:          0          0          0          0          0          0     GICv3  58 Level     ehci_hcd:usb1
+ 68:          0          0          0          0          0          0     GICv3  62 Level     ehci_hcd:usb2
+ 69:          0          0          0          0          0          0     GICv3  60 Level     ohci_hcd:usb3
+ 70:          0          0          0          0          0          0     GICv3  64 Level     ohci_hcd:usb4
+ 71:         37          0          0      33541          0          0     GICv3  44 Level     eth0
+ 72:          0          0          0          0          0          0     GICv3 137 Level     xhci-hcd:usb5
+ 73:      52061          0          0          0          0          0     GICv3 142 Level     xhci-hcd:usb7
+ 74:          0          0          0          0          0          0     GICv3  87 Level     ff680000.rga
+ 75:          0          0          0          0          0          0     GICv3 148 Level     ff660000.video-codec
+ 76:          0          0          0          0          0          0     GICv3 146 Level     ff650000.video-codec
+ 77:          0          0          0          0          0          0     GICv3 145 Level     ff650000.video-codec
+ 78:          0          0          0          0          0          0     GICv3  55 Level     ff940000.hdmi
+ 79:   50468710          0          0          0          0          0  rockchip_gpio_irq  16 Level     adis16505
+IPI0:    629986   29367897    1604808    9830343    7634766    7528717       Rescheduling interrupts
+IPI1:     11040      79148      22290      41912      29091      21995       Function call interrupts
+IPI2:         0          0          0          0          0          0       CPU stop interrupts
+IPI3:         0          0          0          0          0          0       CPU stop (for crash dump) interrupts
+IPI4:   5391338    7612504    8510753   10479370   10766273   10712987       Timer broadcast interrupts
+IPI5:   3040632     140996    4922363      50728      31487      24500       IRQ work interrupts
+IPI6:         0          0          0          0          0          0       CPU wake-up interrupts
+Err:          0
+)contents";
+
+// Tests that the rock pi's /proc/interrupts is parseable.
+TEST(InterruptsStatusTest, RockPi) {
+  InterruptsStatus status;
+  for (int i = 0; i < 2; ++i) {
+    status.Update(kRockPiContents);
+    EXPECT_EQ(status.states()[0].interrupt_number, 23);
+    EXPECT_EQ(status.states()[0].chip_name, "GICv3");
+    EXPECT_THAT(status.states()[0].count,
+                ::testing::ElementsAre(4221703, 2325251, 5737616, 1332002,
+                                       1418781, 1368697));
+    EXPECT_EQ(status.states()[0].hwirq, "30");
+    EXPECT_THAT(status.states()[0].actions,
+                ::testing::ElementsAre("arch_timer"));
+
+    EXPECT_EQ(status.states()[15].interrupt_number, 44);
+    EXPECT_EQ(status.states()[15].chip_name, "GICv3");
+    EXPECT_THAT(status.states()[15].count,
+                ::testing::ElementsAre(0, 0, 3412401, 0, 0, 0));
+    EXPECT_EQ(status.states()[15].hwirq, "75");
+    EXPECT_THAT(status.states()[15].actions,
+                ::testing::ElementsAre("ff914000.iommu", "rkisp1"));
+  }
+
+  status.Update(kRockPiContents2);
+
+  EXPECT_EQ(status.states()[0].interrupt_number, 23);
+  EXPECT_EQ(status.states()[0].chip_name, "GICv3");
+  EXPECT_THAT(status.states()[0].count,
+              ::testing::ElementsAre(1703, 25251, 737616, 2002, 18781, 368697));
+  EXPECT_EQ(status.states()[0].hwirq, "30");
+  EXPECT_THAT(status.states()[0].actions, ::testing::ElementsAre("arch_timer"));
+}
+
+constexpr std::string_view kAustinDesktopContents =
+    R"contents(            CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       CPU8       CPU9       CPU10      CPU11      CPU12      CPU13      CPU14      CPU15      CPU16      CPU17      CPU18      CPU19      
+   8:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-IO-APIC    8-edge      rtc0
+   9:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-IO-APIC    9-fasteoi   acpi
+  14:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-IO-APIC   14-fasteoi   INTC1056:00
+  16:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          4          0          0          0          0          0  IR-IO-APIC   16-fasteoi   peak_pciefd, peak_pciefd, peak_pciefd, peak_pciefd
+  17:          0          0          0          0          0          0          0          0          0       1623          0          0          0          0          0          0          0          0          0          0  IR-IO-APIC   17-fasteoi   snd_hda_intel:card1
+  18:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0        687          0  IR-IO-APIC   18-fasteoi   i801_smbus, snd_hda_intel:card2
+  27:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-IO-APIC   27-fasteoi   idma64.0, i2c_designware.0
+  29:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-IO-APIC   29-fasteoi   idma64.2, i2c_designware.2
+  40:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-IO-APIC   40-fasteoi   idma64.1, i2c_designware.1
+ 120:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  DMAR-MSI    0-edge      dmar0
+ 121:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  DMAR-MSI    1-edge      dmar1
+ 136:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          1          0  IR-PCI-MSI 229376-edge      vmd0
+ 137:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229377-edge      vmd0
+ 138:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229378-edge      vmd0
+ 139:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229379-edge      vmd0
+ 140:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229380-edge      vmd0
+ 141:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229381-edge      vmd0
+ 142:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229382-edge      vmd0
+ 143:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229383-edge      vmd0
+ 144:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229384-edge      vmd0
+ 145:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229385-edge      vmd0
+ 146:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229386-edge      vmd0
+ 147:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229387-edge      vmd0
+ 148:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229388-edge      vmd0
+ 149:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229389-edge      vmd0
+ 150:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229390-edge      vmd0
+ 151:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229391-edge      vmd0
+ 152:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229392-edge      vmd0
+ 153:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229393-edge      vmd0
+ 154:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 229394-edge      vmd0
+ 155:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0        124  IR-PCI-MSI 5767168-edge      thunderbolt
+ 156:        124          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 5767169-edge      thunderbolt
+ 171:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145728-edge      enp6s0
+ 172:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145729-edge      enp6s0
+ 173:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145730-edge      enp6s0
+ 174:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145731-edge      enp6s0
+ 175:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145732-edge      enp6s0
+ 176:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145733-edge      enp6s0
+ 177:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145734-edge      enp6s0
+ 178:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145735-edge      enp6s0
+ 179:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3145736-edge      enp6s0
+ 181:          0   30132468          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 327680-edge      xhci_hcd
+ 182:          0          0          0          0          0     251984          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572864-edge      nvme0q0
+ 183:          0          0          0    1327707          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 376832-edge      ahci[0000:00:17.0]
+ 184:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 33030144-edge      xhci_hcd
+ 185:         42          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572865-edge      nvme0q1
+ 186:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572866-edge      nvme0q2
+ 187:          0          0          4          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572867-edge      nvme0q3
+ 188:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572868-edge      nvme0q4
+ 189:          0          0          0          0          9          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572869-edge      nvme0q5
+ 190:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572870-edge      nvme0q6
+ 191:          0          0          0          0          0          0         60          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572871-edge      nvme0q7
+ 192:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572872-edge      nvme0q8
+ 193:          0          0          0          0          0          0          0          0         43          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572873-edge      nvme0q9
+ 194:          0          0          0          0          0          0          0          0          0         34          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572874-edge      nvme0q10
+ 195:          0          0          0          0          0          0          0          0          0          0         13          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572875-edge      nvme0q11
+ 196:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572876-edge      nvme0q12
+ 197:          0          0          0          0          0          0          0          0          0          0          0          0         58          0          0          0          0          0          0          0  IR-PCI-MSI 1572877-edge      nvme0q13
+ 198:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572878-edge      nvme0q14
+ 199:          0          0          0          0          0          0          0          0          0          0          0          0          0          0         90          0          0          0          0          0  IR-PCI-MSI 1572879-edge      nvme0q15
+ 200:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0         10          0          0          0          0  IR-PCI-MSI 1572880-edge      nvme0q16
+ 201:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0         16          0          0          0  IR-PCI-MSI 1572881-edge      nvme0q17
+ 202:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0         35          0          0  IR-PCI-MSI 1572882-edge      nvme0q18
+ 203:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          1          0  IR-PCI-MSI 1572883-edge      nvme0q19
+ 204:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 1572884-edge      nvme0q20
+ 205:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          1          0          0  IR-PCI-MSI 4194304-edge      enp8s0
+ 206:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0      42818          0  IR-PCI-MSI 4194305-edge      enp8s0-TxRx-0
+ 207:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0      42467  IR-PCI-MSI 4194306-edge      enp8s0-TxRx-1
+ 208:          0          0          0          0          0          0          0          0          0          0          0          0          0      42734          0          0          0          0          0          0  IR-PCI-MSI 4194307-edge      enp8s0-TxRx-2
+ 209:          0          0          0          0          0          0          0          0          0          0          0          0          0          0      42817          0          0          0          0          0  IR-PCI-MSI 4194308-edge      enp8s0-TxRx-3
+ 210:          0          0          0          0          0          0          0          0        352          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 32768-edge      i915
+ 211:          0          0          0          0          0          0          0          0          0          0       2545          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 514048-edge      snd_hda_intel:card0
+ 212:          0          0          0          0          0          0          0          0          0          0          0         50          0          0          0          0          0          0          0          0  IR-PCI-MSI 360448-edge      mei_me
+ 213:          0          0          0          0          0          0          0          0          0          0          0          0     827795          0          0          0          0          0          0          0  IR-PCI-MSI 3670016-edge      iwlwifi:default_queue
+ 214:      44562          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670017-edge      iwlwifi:queue_1
+ 215:          0      37107          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670018-edge      iwlwifi:queue_2
+ 216:          0          0      33498          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670019-edge      iwlwifi:queue_3
+ 217:          0          0          0      29533          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670020-edge      iwlwifi:queue_4
+ 218:          0          0          0          0      21427          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670021-edge      iwlwifi:queue_5
+ 219:          0          0          0          0          0      26244          0          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670022-edge      iwlwifi:queue_6
+ 220:          0          0          0          0          0          0      22479          0          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670023-edge      iwlwifi:queue_7
+ 221:          0          0          0          0          0          0          0      84711          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670024-edge      iwlwifi:queue_8
+ 222:          0          0          0          0          0          0          0          0      22071          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670025-edge      iwlwifi:queue_9
+ 223:          0          0          0          0          0          0          0          0          0      47348          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670026-edge      iwlwifi:queue_10
+ 224:          0          0          0          0          0          0          0          0          0          0      30472          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670027-edge      iwlwifi:queue_11
+ 225:          0          0          0          0          0          0          0          0          0          0          0      31430          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670028-edge      iwlwifi:queue_12
+ 226:          0          0          0          0          0          0          0          0          0          0          0          0      23299          0          0          0          0          0          0          0  IR-PCI-MSI 3670029-edge      iwlwifi:queue_13
+ 227:          0          0          0          0          0          0          0          0          0          0          0          0          0     109672          0          0          0          0          0          0  IR-PCI-MSI 3670030-edge      iwlwifi:queue_14
+ 228:          0          0          0          0          0          0          0          5          0          0          0          0          0          0          0          0          0          0          0          0  IR-PCI-MSI 3670031-edge      iwlwifi:exception
+ 229:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0    4120250          0          0          0  IR-PCI-MSI 524288-edge      nvidia
+ 230:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0     138205          0          0          0          0  IR-PCI-MSI 1048576-edge      nvidia
+ NMI:         92         28        100         10        103         11        107          6        243          6        258          6        157          6        141          6         83         42         24         14   Non-maskable interrupts
+ LOC:    8019100    2732118    7530896    1145194    7715584    1787839    8532279     820360   10887596     601426   12029097     810678   11081674     781984   10455644     576294    5640682    3954630    2196579    1312346   Local timer interrupts
+ SPU:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Spurious interrupts
+ PMI:         92         28        100         10        103         11        107          6        243          6        258          6        157          6        141          6         83         42         24         14   Performance monitoring interrupts
+ IWI:          1          0          0          0          0          0          0          0         24          0         45          0          0          0          0          0          0          0          0          0   IRQ work interrupts
+ RTR:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   APIC ICR read retries
+ RES:     199167     103904     200210      70287     231170      96596     247332      58629     202889      49773     231943      50596     293734      60653     290320      64894     564256     359718     233415     190403   Rescheduling interrupts
+ CAL:    1375045     393024     796832     151717     815698     182423     836372     114075    1150075     122234    1201956     117418     989111     117108     974193     104812     661099     523054     386579     295224   Function call interrupts
+ TLB:     424698      66883     450735      56137     444850      46655     450670      39023     723259      44390     748901      39980     537343      36037     516461      34380     317421     255858     180013     130541   TLB shootdowns
+ TRM:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Thermal event interrupts
+ THR:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Threshold APIC interrupts
+ DFR:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Deferred Error APIC interrupts
+ MCE:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Machine check exceptions
+ MCP:        270        271        271        271        271        271        271        271        271        271        271        271        271        271        271        271        271        271        271        271   Machine check polls
+ ERR:          0
+ MIS:          0
+ PIN:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Posted-interrupt notification event
+ NPI:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Nested posted-interrupt event
+ PIW:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   Posted-interrupt wakeup event
+)contents";
+
+// Tests that Austin's desktop's /proc/interrupts is parsable.
+TEST(InterruptsStatusTest, Desktop) {
+  InterruptsStatus status;
+  status.Update(kAustinDesktopContents);
+  status.Update(kAustinDesktopContents);
+  EXPECT_EQ(status.states()[0].interrupt_number, 8);
+  EXPECT_EQ(status.states()[0].chip_name, "IR-IO-APIC");
+  EXPECT_THAT(status.states()[0].count,
+              ::testing::ElementsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+  EXPECT_EQ(status.states()[0].hwirq, "8-edge");
+  EXPECT_THAT(status.states()[0].actions, ::testing::ElementsAre("rtc0"));
+}
+
+} // aos::testing
diff --git a/aos/starter/kthread.fbs b/aos/starter/kthread.fbs
new file mode 100644
index 0000000..e76185d
--- /dev/null
+++ b/aos/starter/kthread.fbs
@@ -0,0 +1,26 @@
+namespace aos.starter;
+
+table IrqConfig {
+  name: string (id: 0);
+  affinity: [uint8] (id: 1);
+}
+
+enum Scheduler : uint8 {
+  SCHEDULER_OTHER = 0,
+  SCHEDULER_RR = 1,
+  SCHEDULER_FIFO = 2,
+}
+
+table KthreadConfig {
+  name: string (id: 0);
+  priority: int8 (id: 1);
+  scheduler: Scheduler = SCHEDULER_OTHER (id: 2);
+  affinity: [uint8] (id: 3);
+}
+
+table IrqAffinityConfig {
+  irqs: [IrqConfig] (id: 0);
+  kthreads: [KthreadConfig] (id: 1);
+}
+
+root_type IrqAffinityConfig;
diff --git a/aos/starter/starter.sh b/aos/starter/starter.sh
index 9bc7605..d4281c5 100755
--- a/aos/starter/starter.sh
+++ b/aos/starter/starter.sh
@@ -14,7 +14,7 @@
   # We have systemd configured to handle restarting, so just exec.
   export PATH="${PATH}:/home/pi/bin"
   rm -rf /dev/shm/aos
-  exec starterd
+  exec starterd --user=pi
 else
   ROBOT_CODE="${HOME}/bin"
 fi
diff --git a/aos/starter/starterd.cc b/aos/starter/starterd.cc
index f284819..54f1bb4 100644
--- a/aos/starter/starterd.cc
+++ b/aos/starter/starterd.cc
@@ -31,7 +31,7 @@
     constexpr int kUnchanged = -1;
     if (setresgid(/* ruid */ gid, /* euid */ gid,
                   /* suid */ kUnchanged) != 0) {
-      PLOG(FATAL) << "Failed to change GID to " << FLAGS_user;
+      PLOG(FATAL) << "Failed to change GID to " << FLAGS_user << ", group " << gid;
     }
 
     if (setresuid(/* ruid */ uid, /* euid */ uid,
diff --git a/aos/util/top.cc b/aos/util/top.cc
index 79ee2db..8767cd4 100644
--- a/aos/util/top.cc
+++ b/aos/util/top.cc
@@ -10,6 +10,8 @@
 #include "absl/strings/str_format.h"
 #include "absl/strings/str_split.h"
 
+#define PF_KTHREAD 0x00200000
+
 namespace aos::util {
 namespace {
 std::optional<std::string> ReadShortFile(std::string_view file_name) {
@@ -186,13 +188,16 @@
         ProcessStartTime(*proc_stat);
     auto reading_iter = readings_.find(pid);
     if (reading_iter == readings_.end()) {
-      reading_iter = readings_
-                         .insert(std::make_pair(
-                             pid, ProcessReadings{.name = proc_stat->name,
-                                                  .start_time = start_time,
-                                                  .cpu_percent = 0.0,
-                                                  .readings = {}}))
-                         .first;
+      reading_iter =
+          readings_
+              .insert(std::make_pair(
+                  pid, ProcessReadings{.name = proc_stat->name,
+                                       .start_time = start_time,
+                                       .cpu_percent = 0.0,
+                                       .kthread = !!(proc_stat->kernel_flags &
+                                                     PF_KTHREAD),
+                                       .readings = {}}))
+              .first;
     }
     ProcessReadings &process = reading_iter->second;
     // The process associated with the PID has changed; reset the state.
@@ -219,6 +224,10 @@
       process.cpu_percent = 0.0;
     }
   }
+
+  if (on_reading_update_) {
+    on_reading_update_();
+  }
 }
 
 flatbuffers::Offset<ProcessInfo> Top::InfoForProcess(
diff --git a/aos/util/top.h b/aos/util/top.h
index 32ff65d..314e6bd 100644
--- a/aos/util/top.h
+++ b/aos/util/top.h
@@ -95,12 +95,36 @@
 // extremely short-lived loads, this may do a poor job of capturing information.
 class Top {
  public:
+  // A snapshot of the resource usage of a process.
+  struct Reading {
+    aos::monotonic_clock::time_point reading_time;
+    std::chrono::nanoseconds total_run_time;
+    // Memory usage in bytes.
+    uint64_t memory_usage;
+  };
+
+  // All the information we have about a process.
+  struct ProcessReadings {
+    std::string name;
+    aos::monotonic_clock::time_point start_time;
+    // CPU usage is based on the past two readings.
+    double cpu_percent;
+    // True if this is a kernel thread, false if this is a userspace thread.
+    bool kthread;
+    // Last 2 readings
+    aos::RingBuffer<Reading, 2> readings;
+  };
+
   Top(aos::EventLoop *event_loop);
 
   // Set whether to track all the top processes (this will result in us having
   // to track every single process on the system, so that we can sort them).
   void set_track_top_processes(bool track_all) { track_all_ = track_all; }
 
+  void set_on_reading_update(std::function<void()> fn) {
+    on_reading_update_ = std::move(fn);
+  }
+
   // Specify a set of individual processes to track statistics for.
   // This can be changed at run-time, although it may take up to kSamplePeriod
   // to have full statistics on all the relevant processes, since we need at
@@ -116,24 +140,12 @@
   flatbuffers::Offset<TopProcessesFbs> TopProcesses(
       flatbuffers::FlatBufferBuilder *fbb, int n);
 
+  const std::map<pid_t, ProcessReadings> &readings() const { return readings_; }
+
  private:
   // Rate at which to sample /proc/[pid]/stat.
   static constexpr std::chrono::seconds kSamplePeriod{1};
 
-  struct Reading {
-    aos::monotonic_clock::time_point reading_time;
-    std::chrono::nanoseconds total_run_time;
-    uint64_t memory_usage;
-  };
-
-  struct ProcessReadings {
-    std::string name;
-    aos::monotonic_clock::time_point start_time;
-    // CPU usage is based on the past two readings.
-    double cpu_percent;
-    aos::RingBuffer<Reading, 2> readings;
-  };
-
   std::chrono::nanoseconds TotalProcessTime(const ProcStat &proc_stat);
   aos::monotonic_clock::time_point ProcessStartTime(const ProcStat &proc_stat);
   uint64_t RealMemoryUsage(const ProcStat &proc_stat);
@@ -151,6 +163,8 @@
   bool track_all_ = false;
 
   std::map<pid_t, ProcessReadings> readings_;
+
+  std::function<void()> on_reading_update_;
 };
 
 }  // namespace aos::util
diff --git a/build_tests/BUILD b/build_tests/BUILD
index 718e817..ea05c0b 100644
--- a/build_tests/BUILD
+++ b/build_tests/BUILD
@@ -100,15 +100,14 @@
     srcs = ["python_opencv.py"],
     main = "python_opencv.py",
     target_compatible_with = ["@platforms//os:linux"],
-    deps = ["@opencv_contrib_nonfree_amd64//:python_opencv"],
+    deps = ["@pip//opencv_python"],
 )
 
 py_test(
     name = "python_jinja2",
     srcs = ["python_jinja2.py"],
-    srcs_version = "PY2AND3",
     target_compatible_with = ["@platforms//os:linux"],
-    deps = ["@python_jinja2"],
+    deps = ["@pip//jinja2"],
 )
 
 go_binary(
diff --git a/debian/BUILD b/debian/BUILD
index 4291329..ca6d134 100644
--- a/debian/BUILD
+++ b/debian/BUILD
@@ -1,8 +1,4 @@
 load(
-    "//debian:python.bzl",
-    python_debs = "files",
-)
-load(
     ":apache2.bzl",
     apache2_debs = "files",
 )
@@ -39,18 +35,10 @@
     patchelf_debs = "files",
 )
 load(
-    ":matplotlib.bzl",
-    matplotlib_debs = "files",
-)
-load(
     ":arm_frc_gnueabi_deps.bzl",
     arm_frc_gnueabi_deps_debs = "files",
 )
 load(
-    ":python_gtk.bzl",
-    python_gtk_debs = "files",
-)
-load(
     ":gtk_runtime.bzl",
     gtk_runtime_debs = "files",
 )
@@ -98,38 +86,6 @@
 
 package(default_visibility = ["//visibility:public"])
 
-filegroup(
-    name = "matplotlib_patches",
-    srcs = [
-        "matplotlib_init.patch",
-    ],
-    visibility = ["@matplotlib_repo//:__pkg__"],
-)
-
-filegroup(
-    name = "python_shapely_patches",
-    srcs = [
-        "python_shapely_init.patch",
-    ],
-    visibility = ["@python_gtk//:__pkg__"],
-)
-
-filegroup(
-    name = "python_gi_patches",
-    srcs = [
-        "python_gi_init.patch",
-    ],
-    visibility = ["@python_gtk//:__pkg__"],
-)
-
-filegroup(
-    name = "python_geos_patches",
-    srcs = [
-        "python_geos.patch",
-    ],
-    visibility = ["@python_gtk//:__pkg__"],
-)
-
 py_binary(
     name = "download_packages",
     srcs = [
@@ -188,20 +144,6 @@
 )
 
 download_packages(
-    name = "download_python_deps",
-    excludes = [
-        "libblas.so.3",
-        "liblapack.so.3",
-    ],
-    packages = [
-        "python3-dev",
-        "python3-numpy",
-        "python3-scipy",
-    ],
-    target_compatible_with = ["@platforms//os:linux"],
-)
-
-download_packages(
     name = "download_clang_deps",
     excludes = [
         "lib32stdc++6",
@@ -285,24 +227,6 @@
     target_compatible_with = ["@platforms//os:linux"],
 )
 
-download_packages(
-    name = "download_matplotlib_deps",
-    excludes = [
-        "python3-dev",
-        "python3-numpy",
-        "python3-scipy",
-        "x11-common",
-        "fonts-freefont",
-        "python3",
-        "libcups2",
-    ],
-    packages = [
-        "python3-matplotlib",
-        "python3-tk",
-    ],
-    target_compatible_with = ["@platforms//os:linux"],
-)
-
 # This list was obtained by manually looking at the output from:
 # find bazel-out/../../../external/arm_frc_linux_gnueabi_repo/ -executable -type f -exec ldd {} + | sed 's/=>.*//g' | sort -u
 download_packages(
@@ -321,35 +245,6 @@
     target_compatible_with = ["@platforms//os:linux"],
 )
 
-download_packages(
-    name = "download_python_gtk_deps",
-    excludes = [
-        "fonts-freefont",
-        "gsettings-backend",
-        "libpng-dev",
-        "libz-dev",
-        "python3-dev",
-        "python3",
-        "libblas.so.3",
-        "liblapack.so.3",
-        "libstdc++-dev",
-    ],
-    packages = [
-        "libgtk-3-dev",
-        "python3-cairo",
-        "python3-gi",
-        "python3-gi-cairo",
-        "python3-shapely",
-    ],
-    target_compatible_with = ["@platforms//os:linux"],
-)
-
-generate_deb_tarball(
-    name = "python",
-    files = python_debs,
-    target_compatible_with = ["@platforms//os:linux"],
-)
-
 generate_deb_tarball(
     name = "apache2",
     files = apache2_debs,
@@ -405,24 +300,12 @@
 )
 
 generate_deb_tarball(
-    name = "matplotlib",
-    files = matplotlib_debs,
-    target_compatible_with = ["@platforms//os:linux"],
-)
-
-generate_deb_tarball(
     name = "arm_frc_gnueabi_deps",
     files = arm_frc_gnueabi_deps_debs,
     target_compatible_with = ["@platforms//os:linux"],
 )
 
 generate_deb_tarball(
-    name = "python_gtk",
-    files = python_gtk_debs,
-    target_compatible_with = ["@platforms//os:linux"],
-)
-
-generate_deb_tarball(
     name = "gtk_runtime",
     files = gtk_runtime_debs,
     target_compatible_with = ["@platforms//os:linux"],
diff --git a/debian/gflags.BUILD b/debian/gflags.BUILD
deleted file mode 100644
index f1e3b2c..0000000
--- a/debian/gflags.BUILD
+++ /dev/null
@@ -1,67 +0,0 @@
-py_library(
-    name = "gflags",
-    srcs = [
-        "gflags.py",
-        "gflags2man.py",
-        "gflags_validators.py",
-    ],
-    visibility = ["//visibility:public"],
-)
-
-py_library(
-    name = "gflags_googletest",
-    srcs = [
-        "tests/gflags_googletest.py",
-    ],
-)
-
-py_test(
-    name = "gflags_validators_test",
-    size = "small",
-    srcs = [
-        "tests/gflags_validators_test.py",
-    ],
-    deps = [
-        ":gflags",
-        ":gflags_googletest",
-    ],
-)
-
-py_library(
-    name = "flags_modules_for_testing",
-    srcs = [
-        "tests/flags_modules_for_testing/__init__.py",
-        "tests/flags_modules_for_testing/module_bar.py",
-        "tests/flags_modules_for_testing/module_baz.py",
-        "tests/flags_modules_for_testing/module_foo.py",
-    ],
-    deps = [
-        ":gflags",
-    ],
-)
-
-py_test(
-    name = "gflags_unittest",
-    size = "small",
-    srcs = [
-        "tests/gflags_unittest.py",
-    ],
-    deps = [
-        ":flags_modules_for_testing",
-        ":gflags",
-        ":gflags_googletest",
-    ],
-)
-
-py_test(
-    name = "gflags_helpxml_test",
-    size = "small",
-    srcs = [
-        "tests/gflags_helpxml_test.py",
-    ],
-    deps = [
-        ":flags_modules_for_testing",
-        ":gflags",
-        ":gflags_googletest",
-    ],
-)
diff --git a/debian/glog.BUILD b/debian/glog.BUILD
deleted file mode 100644
index 8de9b80..0000000
--- a/debian/glog.BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-py_library(
-    name = "glog",
-    srcs = [
-        "glog.py",
-    ],
-    visibility = ["//visibility:public"],
-    deps = [
-        "//external:python-gflags",
-    ],
-)
diff --git a/debian/halide.BUILD b/debian/halide.BUILD
index 972df97..a38d910 100644
--- a/debian/halide.BUILD
+++ b/debian/halide.BUILD
@@ -8,7 +8,7 @@
 
 cc_library(
     name = "gengen",
-    srcs = ["tools/GenGen.cpp"],
+    srcs = ["share/Halide/tools/GenGen.cpp"],
     visibility = ["//visibility:public"],
     deps = [
         ":halide",
@@ -29,7 +29,7 @@
     name = "build_files",
     srcs = [
         "lib/libHalide.a",
-        "tools/GenGen.cpp",
+        "share/Halide/tools/GenGen.cpp",
     ] + glob(["include/*.h"]),
     visibility = ["//visibility:public"],
 )
diff --git a/debian/matplotlib.bzl b/debian/matplotlib.bzl
deleted file mode 100644
index 4e9feb8..0000000
--- a/debian/matplotlib.bzl
+++ /dev/null
@@ -1,209 +0,0 @@
-files = {
-    "blt_2.5.3+dfsg-4.1_amd64.deb": "1f4fe70b93976daf7cb728f3fb7561edbb0c78360f053a521ee9075f2ec5c809",
-    "coreutils_8.32-4+b1_amd64.deb": "3558a412ab51eee4b60641327cb145bb91415f127769823b68f9335585b308d4",
-    "fontconfig-config_2.13.1-4.2_all.deb": "48afb6ad7d15e6104a343b789f73697301ad8bff77b69927bc998f5a409d8e90",
-    "fonts-croscore_20201225-1_all.deb": "64904820b729ff40038f85683004e3b94b328d969bc0fbba263c58d635452923",
-    "fonts-dejavu-core_2.37-2_all.deb": "1f67421437b6eb18669d2868e3e02cb88668683d635198142f48aacc5b397118",
-    "fonts-freefont-otf_20120503-10_all.deb": "0b63996c80c6c660424af6d3832818e647960d6f65a51de010bb57dd0762faa7",
-    "fonts-freefont-ttf_20120503-10_all.deb": "4ca1c21ebc479198a3a5879d236c8317d6f7b2f1c403f7890e24c02eead05615",
-    "fonts-liberation2_2.1.3-1_all.deb": "e0805f0085132f5e6dd30f88c0d7260caf1e5450832fe2e3988a20fa9fa2150e",
-    "fonts-liberation_1.07.4-11_all.deb": "efd381517f958b01969343634ffcbdd60056be7779af84c6f53a005090430204",
-    "fonts-lyx_2.3.6-1_all.deb": "c6a1e4105d3c4edc246e4740560c9eda0e5fffec72dd03de256cf10f669fbec8",
-    "fonts-texgyre_20180621-3.1_all.deb": "cb7e9a4b2471cfdd57194c16364f9102f0639816a2662fed4b30d2a158747076",
-    "fonts-urw-base35_20200910-1_all.deb": "f95a139adb7f1b60626e76d4d45d1b35aad1bc2c2597394c291ef5f84b5dcb43",
-    "libattr1_2.4.48-6_amd64.deb": "af3c3562eb2802481a2b9558df1b389f3c6d9b1bf3b4219e000e05131372ebaf",
-    "libbrotli1_1.0.9-2+b2_amd64.deb": "65ca7d8b03e9dac09c5d544a89dd52d1aeb74f6a19583d32e4ff5f0c77624c24",
-    "libbsd0_0.11.3-1_amd64.deb": "284a7b8dcfcad74770f57360721365317448b38ab773db542bf630e94e60c13e",
-    "libdeflate0_1.7-1_amd64.deb": "dadaf0d28360f6eb21ad389b2e0f12f8709c9de539b28de9c11d7ec7043dec95",
-    "libfontconfig1_2.13.1-4.2_amd64.deb": "b92861827627a76e74d6f447a5577d039ef2f95da18af1f29aa98fb96baea4c1",
-    "libfreetype6_2.10.4+dfsg-1_amd64.deb": "e95396fc3cc806b2b95d9a00b4226eb464bc3ef4817c798749a0dd582546e5bc",
-    "libimagequant0_2.12.2-1.1_amd64.deb": "cca1a4d3c24358c75964c720445a1ba50467aa52af7fb82a387f460a4a065096",
-    "libjbig0_2.1-3.1+b2_amd64.deb": "9646d69eefce505407bf0437ea12fb7c2d47a3fd4434720ba46b642b6dcfd80f",
-    "libjpeg62-turbo_2.0.6-4_amd64.deb": "28de780a1605cf501c3a4ebf3e588f5110e814b208548748ab064100c32202ea",
-    "libjs-jquery-ui_1.12.1+dfsg-8+deb11u1_all.deb": "9a2cea1140b526daa97e51f91bea63cb05679c4206d70e926915836d4f72be58",
-    "liblcms2-2_2.12~rc1-2_amd64.deb": "0608ecb6ed258814e390b52b3fb50f2a6d3239b5ecb1086292ae08be00a67b0f",
-    "libmd0_1.0.3-3_amd64.deb": "9e425b3c128b69126d95e61998e1b5ef74e862dd1fc953d91eebcc315aea62ea",
-    "libpng16-16_1.6.37-3_amd64.deb": "7d5336af395d1f658d0e66d74d0e1f4c632028750e7e04314d1a650e0317f3d6",
-    "libtcl8.6_8.6.11+dfsg-1_amd64.deb": "785df3d81010a67ded4a2c216c7b99657c6ab3d1ba7369119894abc851e5bb0c",
-    "libtiff5_4.2.0-1_amd64.deb": "ec12fc6a05a30f8f98878dbb6d9b356d170d5f6239fb68ba6a1dc9eb89ba3194",
-    "libtk8.6_8.6.11-2_amd64.deb": "20d70721a5d539266a8736800378398d088419b986b5313ca811203284690f12",
-    "libwebp6_0.6.1-2.1_amd64.deb": "52bfd0f8d3a1bbd2c25fcd72fab857d0f24aea35874af68e057dde869ae3902c",
-    "libwebpdemux2_0.6.1-2.1_amd64.deb": "bba38f28d461b6b2e94fc51fff34e0d179c2dea3b0c31a77de788f2023be6614",
-    "libwebpmux3_0.6.1-2.1_amd64.deb": "78486e53903cbf422dfe04a33e5481c56c82198a2bfa307f2066e616477395f5",
-    "libx11-6_1.7.2-1_amd64.deb": "086bd667fc07369472a923da015d182bb0c15a72228a5c0e6ddbcbeaab70acd2",
-    "libx11-data_1.7.2-1_all.deb": "049b7eabced516acfdf44a5e81c26d108b16e4987e5d7604ea53eaade74027fb",
-    "libxau6_1.0.9-1_amd64.deb": "679db1c4579ec7c61079adeaae8528adeb2e4bf5465baa6c56233b995d714750",
-    "libxcb1_1.14-3_amd64.deb": "d5e0f047ed766f45eb7473947b70f9e8fddbe45ef22ecfd92ab712c0671a93ac",
-    "libxdmcp6_1.1.2-3_amd64.deb": "ecb8536f5fb34543b55bb9dc5f5b14c9dbb4150a7bddb3f2287b7cab6e9d25ef",
-    "libxext6_1.3.3-1.1_amd64.deb": "dc1ff8a2b60c7dd3c8917ffb9aa65ee6cda52648d9150608683c47319d1c0c8c",
-    "libxft2_2.3.2-2_amd64.deb": "cd71384b4d511cba69bcee29af326943c7ca12450765f44c40d246608c779aad",
-    "libxrender1_0.9.10-1_amd64.deb": "3ea17d07b5aa89012130e2acd92f0fc0ea67314e2f5eab6e33930ef688f48294",
-    "libxss1_1.2.3-1_amd64.deb": "85cce16368f08a878fa892fbc54520fc654d00769cde6d300b8b802734a993c0",
-    "python-matplotlib-data_3.3.4-1_all.deb": "3ae74d712766f261e41502a712ec8b471f7514b7fc4ae03fb6df539a553b61ff",
-    "python3-cycler_0.10.0-3_all.deb": "87022b0aaf76b345ed80a6737326592a6009035a0eb694f475030ad325e91262",
-    "python3-dateutil_2.8.1-6_all.deb": "59d2e32149471adee915d6b721726612c873a83f262a041899573a1867e9e450",
-    "python3-kiwisolver_1.3.1-1+b1_amd64.deb": "ace5f22edfc057f252a57da7c04145ae298dddc658e3e72dbe7bddd39e03838a",
-    "python3-matplotlib_3.3.4-1_amd64.deb": "e411a5de1fb43c3076697b48ec7eb36d4c98e6359d6717b34adfdd40b49fb9d5",
-    "python3-pil.imagetk_8.1.2+dfsg-0.3_amd64.deb": "b0e98d9c7ebdee941b0a24f40c7b29b4cb4c8759e04c478da1d0380ebf956c1e",
-    "python3-pil_8.1.2+dfsg-0.3_amd64.deb": "a0e44fe855f0b4f7a175fe4dad7db27a18adc8a0119e930010bc95641c66237a",
-    "python3-pyparsing_2.4.7-1_all.deb": "bec23dc18bb37357f34e77e3f9590224b2d0f3bb4d9bb099600fa2d547800aa3",
-    "python3-six_1.16.0-2_all.deb": "acd4271ba56e42298b9fab0fcdad8f319970b50d1026f8469eed968a20f28761",
-    "python3-tk_3.9.2-1_amd64.deb": "272db7708d60675b6aaea98aef64131e2bc9ec443ad677ef247d341632d9ef07",
-    "sensible-utils_0.0.14_all.deb": "b9a447dc4ec8714196b037e20a2209e62cd669f5450222952f259bda4416b71f",
-    "tk8.6-blt2.5_2.5.3+dfsg-4.1_amd64.deb": "f12f8dd7ed62386d4a798ee84df7fd976b9ba4d9a930ba5b5d4bf48bd16437ca",
-    "ttf-bitstream-vera_1.10-8.1_all.deb": "ba622edf73744b2951bbd20bfc113a1a875a9b0c6fed1ac9e9c7f4b54dd8a048",
-    "ucf_3.0043_all.deb": "ebef6bcd777b5c0cc2699926f2159db08433aed07c50cb321fd828b28c5e8d53",
-}
-
-def build_matplotlib(version, tkinter_py_version = None, copy_shared_files = True):
-    """Creates a py_library rule for matplotlib for the given python version.
-
-    See debian/matplotlib.BUILD for the usage.
-
-    All the rules generated by this will be suffixed by version. Only one
-    instance of this macro should set copy_shared_files, which generate the
-    files that are shared between python versions.
-
-    tkinter_py_version is used because for the Python3 instance, some files
-    are in folders named python3 and some are in folders named python3.5...
-
-    version numbers should both be strings.
-    """
-    if tkinter_py_version == None:
-        tkinter_py_version = version
-
-    native.genrule(
-        name = "patch_init" + version,
-        srcs = [
-            "usr/lib/python" + version + "/dist-packages/matplotlib/__init__.py",
-            "@//debian:matplotlib_patches",
-        ],
-        outs = [version + "/matplotlib/__init__.py"],
-        cmd = " && ".join([
-            "cp $(location usr/lib/python" + version + "/dist-packages/matplotlib/__init__.py) $@",
-            "readonly PATCH=\"$$(readlink -f $(location @patch))\"",
-            "readonly FILE=\"$$(readlink -f $(location @//debian:matplotlib_patches))\"",
-            "(cd $(@D) && \"$${PATCH}\" -p1 < \"$${FILE}\") > /dev/null",
-        ]),
-        tools = [
-            "@patch",
-        ],
-    )
-
-    _src_files = native.glob(
-        include = ["usr/lib/python" + version + "/dist-packages/**/*.py"],
-        exclude = [
-            "usr/lib/python" + version + "/dist-packages/matplotlib/__init__.py",
-        ],
-    )
-
-    _data_files = native.glob([
-        "usr/share/matplotlib/mpl-data/**",
-        "usr/share/tcltk/**",
-    ])
-
-    _src_copied = ["/".join([version] + f.split("/")[4:]) for f in _src_files]
-
-    _builtin_so_files = native.glob([
-        "usr/lib/python" + version + "/dist-packages/**/*x86_64-linux-gnu.so",
-        "usr/lib/python" + tkinter_py_version + "/lib-dynload/*.so",
-    ])
-
-    _system_so_files = native.glob([
-        "usr/lib/**/*.so*",
-        "lib/x86_64-linux-gnu/**/*.so*",
-    ])
-
-    _builtin_so_copied = ["/".join([version] + f.split("/")[4:]) for f in _builtin_so_files]
-
-    rpath_prefix = "rpathed" + version + "/"
-
-    _system_so_copied = [rpath_prefix + f for f in _system_so_files]
-
-    _builtin_rpaths = [":".join([
-        "\\$$ORIGIN/%s" % rel,
-        "\\$$ORIGIN/%s/%s/usr/lib/x86_64-linux-gnu" % (rel, rpath_prefix),
-        "\\$$ORIGIN/%s/%s/usr/lib" % (rel, rpath_prefix),
-        "\\$$ORIGIN/%s/%s/lib/x86_64-linux-gnu" % (rel, rpath_prefix),
-    ]) for rel in ["/".join([".." for _ in so.split("/")[1:]]) for so in _builtin_so_copied]]
-
-    _system_rpaths = [":".join([
-        "\\$$ORIGIN/%s/%s/usr/lib/x86_64-linux-gnu" % (rel, rpath_prefix),
-        "\\$$ORIGIN/%s/%s/lib/x86_64-linux-gnu" % (rel, rpath_prefix),
-    ]) for rel in ["/".join([".." for _ in so.split("/")[1:]]) for so in _system_so_copied]]
-
-    native.genrule(
-        name = "run_patchelf_builtin" + version,
-        srcs = _builtin_so_files,
-        outs = _builtin_so_copied,
-        cmd = "\n".join(
-            [
-                "cp $(location %s) $(location %s)" % (src, dest)
-                for src, dest in zip(_builtin_so_files, _builtin_so_copied)
-            ] +
-            ["$(location @patchelf) --set-rpath %s $(location %s)" % (rpath, so) for rpath, so in zip(_builtin_rpaths, _builtin_so_copied)],
-        ),
-        tools = [
-            "@patchelf",
-        ],
-    )
-
-    native.genrule(
-        name = "run_patchelf_system" + version,
-        srcs = _system_so_files,
-        outs = _system_so_copied,
-        cmd = "\n".join(
-            [
-                "cp $(location %s) $(location %s)" % (src, dest)
-                for src, dest in zip(_system_so_files, _system_so_copied)
-            ] +
-            ["$(location @patchelf) --set-rpath %s $(location %s)" % (rpath, so) for rpath, so in zip(_system_rpaths, _system_so_copied)],
-        ),
-        tools = [
-            "@patchelf",
-        ],
-    )
-
-    native.genrule(
-        name = "copy_files" + version,
-        srcs = _src_files,
-        outs = _src_copied,
-        cmd = " && ".join(["cp $(location %s) $(location %s)" % (src, dest) for src, dest in zip(
-            _src_files,
-            _src_copied,
-        )]),
-    )
-
-    if copy_shared_files:
-        native.genrule(
-            name = "create_rc" + version,
-            srcs = ["etc/matplotlibrc"],
-            outs = ["usr/share/matplotlib/mpl-data/matplotlibrc"],
-            cmd = "cat $< > $@",
-        )
-
-    native.py_library(
-        name = "matplotlib" + version,
-        srcs = _src_copied + [
-            version + "/matplotlib/__init__.py",
-        ] + native.glob(
-            include = ["usr/lib/python" + tkinter_py_version + "/**/*.py"],
-        ),
-        data = _data_files + _builtin_so_copied + _system_so_copied + [
-        ] + native.glob(["etc/**", "usr/share/fonts/**"]),
-        imports = [
-            "rpathed3/usr/lib/python" + version + "/dist-packages",
-            "rpathed3/usr/lib/python" + version + ".9/lib-dynload",
-            version,
-            ".",
-            "usr/lib/python" + tkinter_py_version,
-        ],
-        target_compatible_with = [
-            "@platforms//cpu:x86_64",
-            "@//tools/platforms/python:debian_bundled_python",
-        ],
-        visibility = ["//visibility:public"],
-        deps = [
-            "@python_repo//:numpy",
-        ],
-    )
diff --git a/debian/opencv_python.BUILD b/debian/opencv_python.BUILD
deleted file mode 100644
index 8d2ef2a..0000000
--- a/debian/opencv_python.BUILD
+++ /dev/null
@@ -1,16 +0,0 @@
-py_library(
-    name = "python_opencv",
-    srcs = glob(["**/*.py"]),
-    data = glob(
-        include = ["**/*"],
-        exclude = ["**/*.py"],
-    ),
-    imports = ["."],
-    target_compatible_with = [
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-    deps = [
-        "@python_repo//:numpy",
-    ],
-)
diff --git a/debian/osqp_python.BUILD b/debian/osqp_python.BUILD
deleted file mode 100644
index b790a75..0000000
--- a/debian/osqp_python.BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-py_library(
-    name = "python_osqp",
-    srcs = glob(["**/*.py"]),
-    data = glob(
-        include = ["**/*"],
-        exclude = ["**/*.py"],
-    ),
-    imports = ["."],
-    target_compatible_with = [
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-    deps = [
-        "@python_repo//:numpy",
-        "@python_repo//:scipy",
-        "@qdldl_amd64//:python_qdldl",
-    ],
-)
diff --git a/debian/packages.bzl b/debian/packages.bzl
index dfb4ed8..6b039f8 100644
--- a/debian/packages.bzl
+++ b/debian/packages.bzl
@@ -1,4 +1,4 @@
-load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
+load("@rules_pkg//:pkg.bzl", "pkg_tar")
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
 
 # In order to use deb packages in the build you have to follow these steps:
diff --git a/debian/python.BUILD b/debian/python.BUILD
deleted file mode 100644
index 803e4bd..0000000
--- a/debian/python.BUILD
+++ /dev/null
@@ -1,108 +0,0 @@
-package(default_visibility = ["@//debian:__pkg__"])
-
-cc_library(
-    name = "python3.9_lib",
-    srcs = [
-        "usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0",
-    ],
-    hdrs = glob(["usr/include/**/*.h"]),
-    includes = [
-        "usr/include/",
-        "usr/include/python3.9/",
-    ],
-    target_compatible_with = ["@platforms//cpu:x86_64"],
-    visibility = ["//visibility:public"],
-)
-
-cc_library(
-    name = "python3.9_f2py",
-    srcs = [
-        "usr/lib/python3/dist-packages/numpy/f2py/src/fortranobject.c",
-    ],
-    hdrs = [
-        "usr/lib/python3/dist-packages/numpy/f2py/src/fortranobject.h",
-    ],
-    copts = [
-        "-Wno-error",
-        "-Wno-parentheses-equality",
-    ],
-    includes = [
-        "usr/lib/python3/dist-packages/numpy/f2py/src/",
-    ],
-    visibility = ["//visibility:public"],
-    deps = [
-        ":python3.9_lib",
-    ],
-)
-
-filegroup(
-    name = "all_files",
-    srcs = glob(["**"]),
-    visibility = ["//visibility:public"],
-)
-
-genrule(
-    name = "copy_f2py",
-    srcs = ["usr/bin/f2py"],
-    outs = ["f2py.py"],
-    cmd = "cp $< $@",
-    executable = True,
-)
-
-py_binary(
-    name = "f2py",
-    srcs = ["f2py.py"],
-    visibility = ["//visibility:public"],
-)
-
-py_library(
-    name = "scipy",
-    srcs = glob([
-        "usr/lib/python3/dist-packages/scipy/**/*.py",
-    ]),
-    data = glob(
-        [
-            "usr/lib/python3/dist-packages/scipy/**/*",
-        ],
-        exclude = [
-            "usr/lib/python3/dist-packages/scipy/**/*.py",
-            "usr/lib/python3/dist-packages/scipy/io/tests/**",
-        ],
-    ),
-    imports = [
-        "usr/lib/python3/dist-packages",
-    ],
-    target_compatible_with = [
-        "@platforms//os:linux",
-        "@platforms//cpu:x86_64",
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-    deps = [
-        ":numpy",
-    ],
-)
-
-py_library(
-    name = "numpy",
-    srcs = glob([
-        "usr/lib/python3/dist-packages/numpy/**/*.py",
-    ]),
-    data = glob(
-        [
-            "usr/lib/python3/dist-packages/numpy/**/*",
-        ],
-        exclude = [
-            "usr/lib/python3/dist-packages/numpy/**/*.py",
-        ],
-    ),
-    imports = [
-        "usr/lib/python3/dist-packages",
-    ],
-    target_compatible_with = [
-        "@platforms//os:linux",
-        "@platforms//cpu:x86_64",
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-)
diff --git a/debian/python.bzl b/debian/python.bzl
deleted file mode 100644
index 2ef89f7..0000000
--- a/debian/python.bzl
+++ /dev/null
@@ -1,50 +0,0 @@
-files = {
-    "libblas3_3.9.0-3_amd64.deb": "489238f1d2f65dad98d134e5d7fec2a857422d7d2c8af029fc277cff0eec92d7",
-    "libdb5.3_5.3.28+dfsg1-0.8_amd64.deb": "00b9e63e287f45300d4a4f59b6b88e25918443c932ae3e5845d5761ae193c530",
-    "libexpat1-dev_2.2.10-2_amd64.deb": "e9ec11e23a308b252dfb8e3a8a501f91e78b226bdde1b6ce0a938889c0b1ff3f",
-    "libexpat1_2.2.10-2_amd64.deb": "eda6663f34375a9456c8c701002f1271bc90ac2627b9fb0892474e65eae1b668",
-    "libffi7_3.3-6_amd64.deb": "30ca89bfddae5fa6e0a2a044f22b6e50cd17c4bc6bc850c579819aeab7101f0f",
-    "libgdbm-compat4_1.19-2_amd64.deb": "e62caed68b0ffaa03b5fa539d6fdc08c4151f66236d5878949bead0b71b7bb09",
-    "libgdbm6_1.19-2_amd64.deb": "e54cfe4d8b8f209bb7df31a404ce040f7c2f9b1045114a927a7e1061cdf90727",
-    "libgfortran5_10.2.1-6_amd64.deb": "6fe41d04ea9ef8c5c684b14585caa7a4a7e04ad6805d59cdd29016960b737123",
-    "libjs-jquery_3.5.1+dfsg+~3.5.5-7_all.deb": "b57b83ab5a3b1f055860bdf86f9316124f1da3c75fbba5dabbcb3cd3707c95ed",
-    "libjs-sphinxdoc_3.4.3-2_all.deb": "f789debe1bc0164064d502d0230c98f9c5857331e43e02e9487fc42c4068bea9",
-    "libjs-underscore_1.9.1~dfsg-3_all.deb": "85fb8fd215bc742f1a17285b3bf38a2a0d0aa5b2c2188f8d7dec2323d2ab945d",
-    "liblapack3_3.9.0-3_amd64.deb": "7fc4cd55ca777dbe0745bd167abebed0b5d64b5cdff8900fec2ae579859fbade",
-    "liblbfgsb0_3.0+dfsg.3-9_amd64.deb": "b7bfa24c9b03e825ed3a970b3fa2b8f5369be1bba42abef2e338e4328fddb455",
-    "libmpdec3_2.5.1-1_amd64.deb": "816404866f754d5662d69db50072018dccb78cf372fc38e0be961fab0f57d741",
-    "libncursesw6_6.2+20201114-2_amd64.deb": "ee3cd315dfa18865cf888ba6813a552077a4f3d1439dd225e4a0d0fee53aadc2",
-    "libperl5.32_5.32.1-4+deb11u2_amd64.deb": "224cafe65968deb83168113b74dff2d2f13b115a41d99eb209ed3b8f981df0b3",
-    "libpython3-dev_3.9.2-3_amd64.deb": "8e81c69eb7f17cd2fc7935d76e97882d1532ff8cdf45db731ee2c7f217155a37",
-    "libpython3-stdlib_3.9.2-3_amd64.deb": "f903a5e24fd155cf09f25823a75c95a7de03a408ab699fe05c2c2930cbe018e3",
-    "libpython3.9-dev_3.9.2-1_amd64.deb": "3a2172f6d926cf2e98a1a2e24f5aecdc3bcd56fa7c6d9f7a3fc4892f67377d5f",
-    "libpython3.9-minimal_3.9.2-1_amd64.deb": "594283526d67e03e3c4e96534f6bfe5da1bf41da5d49360553c9b79fbf08c4ab",
-    "libpython3.9-stdlib_3.9.2-1_amd64.deb": "32ac64d959ac2dbc5bb289532fc9834dab8f671b5455695a3a9315aad973c65b",
-    "libpython3.9_3.9.2-1_amd64.deb": "97647869e7e9db643a7eeab949b7d140f9a175814fcc94289be70d426aa5fd1a",
-    "libquadmath0_10.2.1-6_amd64.deb": "a9a5e1f53b7e27a3f2b8388929bb622d3c6c35a4e42ac166697444e5ed662fd5",
-    "libreadline8_8.1-1_amd64.deb": "162ba9fdcde81b5502953ed4d84b24e8ad4e380bbd02990ab1a0e3edffca3c22",
-    "libsqlite3-0_3.34.1-3_amd64.deb": "a0b8d3acf4a0483048637637d269be93af48d5c16f6f139f53edd13384ad4686",
-    "libstdc++6_10.2.1-6_amd64.deb": "5c155c58935870bf3b4bfe769116841c0d286a74f59eccfd5645693ac23f06b1",
-    "libtinfo6_6.2+20201114-2_amd64.deb": "aeaf942c71ecc0ed081efdead1a1de304dcd513a9fc06791f26992e76986597b",
-    "libuuid1_2.36.1-8_amd64.deb": "94f13f58ac45ae850559e6bfe1a02be72566c66761e628a2599cc85066cb84d3",
-    "mailcap_3.69_all.deb": "63fa5520f05d2aea5ca23eee95981a5e029608e1186ded4143470c8f84184158",
-    "media-types_4.0.0_all.deb": "f9835dcf3cdbaf163104d4e511c9c4e0f41a56822e147e57f28f749fcbf7d44c",
-    "mime-support_3.66_all.deb": "b964e671e6c47674879a3e54130b6737e8760fbd3da6afcc015faa174af98ba0",
-    "perl-modules-5.32_5.32.1-4+deb11u2_all.deb": "6fa15be322c3c89ec4a07d704ad58d4a2d1aabf866135a859f6d8d58c59e9df4",
-    "perl_5.32.1-4+deb11u2_amd64.deb": "1cebc4516ed7c240b812c7bdd7e6ea0810f513152717ca17ce139ee0dfbc7b0d",
-    "python3-decorator_4.4.2-2_all.deb": "ad81bca4874bd0a622422b7f007c411c1fb416acff21dbc1613bf2df563f0dc4",
-    "python3-dev_3.9.2-3_amd64.deb": "d378f376dd8443b35198326cc875fa93d541b61ef4ae29354c65ac6fe05da485",
-    "python3-distutils_3.9.2-1_all.deb": "05ec4080e0f05ba6b1c339d89c97f6343919be450b66cf4cfb215f54dcb85e58",
-    "python3-lib2to3_3.9.2-1_all.deb": "802c198e5dd0b5af85a6937e426a85d616680785e8d18148fac451281a83a9a9",
-    "python3-minimal_3.9.2-3_amd64.deb": "159320ef8a2d740dcc2245c7f0ac0e678b2796354044c90760fe2a4c6ef117b4",
-    "python3-numpy_1.19.5-1_amd64.deb": "fe8c8955be71be9f744729b73c73cc6bc01184daf253604f1d3ee16a8d64d30d",
-    "python3-pkg-resources_52.0.0-4_all.deb": "e81cb1b4a3aa739fa5bcfceb043c3bd47233b4f5a36f88ad2de1f05a83d6ec2b",
-    "python3-scipy_1.6.0-2_amd64.deb": "8f46ff26b1e6de1fa64626de2464d5743f9630e16a2b3b2b4d555abc2525c76a",
-    "python3.9-dev_3.9.2-1_amd64.deb": "7e93f240abe1c78c10d69ccec41ec5769763d99cebfd4e91407348520b18d908",
-    "python3.9-minimal_3.9.2-1_amd64.deb": "5d6003c5c20223a2547ac4976e09a9ec778dbe351507a6292bef3b60df5f4aa7",
-    "python3.9_3.9.2-1_amd64.deb": "12f8b47632cc26b986bfc4c882fc98e7036d406143ae8bb1dbfff6b61d7c8993",
-    "python3_3.9.2-3_amd64.deb": "6d9375916c5c0d670df708bed3e8c033ce4b197a580d536ce39d1170c67a4c95",
-    "readline-common_8.1-1_all.deb": "3f947176ef949f93e4ad5d76c067d33fa97cf90b62ee0748acb4f5f64790edc8",
-    "tzdata_2021a-1+deb11u2_all.deb": "4a34cbe17d391e6351386f3530b7ffd096c2cc8582e970f745addc636fa7c397",
-    "zlib1g-dev_1.2.11.dfsg-2_amd64.deb": "a36b74415b32513dab9a2fa56e7d215f5e5d0185df6939e483267cef15e2eaf5",
-}
diff --git a/debian/python_geos.patch b/debian/python_geos.patch
deleted file mode 100644
index 963f043..0000000
--- a/debian/python_geos.patch
+++ /dev/null
@@ -1,15 +0,0 @@
---- a/geos.py	2018-10-17 23:02:05.000000000 -0700
-+++ b/geos.py	2018-10-17 23:16:33.017257372 -0700
-@@ -39,6 +39,11 @@ def load_dll(libname, fallbacks=None):
-         except OSError:
-             LOG.warn("Failed `CDLL(%s)`", lib)
-             pass
-
-+        base = os.path.dirname(os.path.dirname(__file__))
-+        try:
-+            return CDLL(os.path.join(base, 'rpathed', 'usr', 'lib', 'x86_64-linux-gnu', lib))
-+        except OSError:
-+            pass
-     if not dll and fallbacks is not None:
-         for name in fallbacks:
-             try:
diff --git a/debian/python_gi_init.patch b/debian/python_gi_init.patch
deleted file mode 100644
index 50cb6ac..0000000
--- a/debian/python_gi_init.patch
+++ /dev/null
@@ -1,25 +0,0 @@
---- a/__init__.py	1969-12-31 16:00:00.000000000 -0800
-+++ b/__init__.py	2018-10-17 21:45:04.908201161 -0700
-@@ -29,6 +29,22 @@ import os
- import importlib
- import types
- 
-+_base = os.path.dirname(os.path.dirname(__file__))
-+os.environ['GI_TYPELIB_PATH'] = os.path.join(_base, 'usr', 'lib',
-+                                             'x86_64-linux-gnu',
-+                                             'girepository-1.0')
-+
-+# Tell fontconfig where to find the sandboxed font files.
-+os.environ["FONTCONFIG_PATH"] = os.path.join(_base, "etc/fonts/")
-+os.environ["FONTCONFIG_FILE"] = os.path.join(_base, "etc/fonts/fonts.conf")
-+# The sysroot here needs to be "/". If it were _base, then the font caches
-+# would contain _base-relative paths in them. Unfortunately pango interprets
-+# those as absolute paths and ends up failing to find all fonts.
-+os.environ["FONTCONFIG_SYSROOT"] = "/"
-+os.environ["GDK_PIXBUF_MODULEDIR"] = os.path.join(_base, "rpathed", "usr", "lib", "x86_64-linux-gnu", "gdk-pixbuf-2.0", "2.10.0", "loaders")
-+os.environ["GDK_PIXBUF_MODULE_FILE"] = os.path.join(os.environ["GDK_PIXBUF_MODULEDIR"], "loaders.cache")
-+os.system(os.path.join(_base, "usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders") + " --update-cache")
-+
- _static_binding_error = ('When using gi.repository you must not import static '
-                          'modules like "gobject". Please change all occurrences '
-                          'of "import gobject" to "from gi.repository import GObject". '
diff --git a/debian/python_gtk.BUILD b/debian/python_gtk.BUILD
deleted file mode 100644
index 226d17a..0000000
--- a/debian/python_gtk.BUILD
+++ /dev/null
@@ -1,171 +0,0 @@
-genrule(
-    name = "patch_gi_init",
-    srcs = [
-        "usr/lib/python3/dist-packages/gi/__init__.py",
-        "@//debian:python_gi_patches",
-    ],
-    outs = ["gi/__init__.py"],
-    cmd = " && ".join([
-        "cp $(location usr/lib/python3/dist-packages/gi/__init__.py) $@",
-        "readonly PATCH=\"$$(readlink -f $(location @patch))\"",
-        "readonly FILE=\"$$(readlink -f $(location @//debian:python_gi_patches))\"",
-        "(cd $(@D) && \"$${PATCH}\" -p1 < \"$${FILE}\") > /dev/null",
-    ]),
-    tools = [
-        "@patch",
-    ],
-)
-
-genrule(
-    name = "patch_shapely_init",
-    srcs = [
-        "usr/lib/python3/dist-packages/shapely/__init__.py",
-        "@//debian:python_shapely_patches",
-    ],
-    outs = ["shapely/__init__.py"],
-    cmd = " && ".join([
-        "cp $(location usr/lib/python3/dist-packages/shapely/__init__.py) $@",
-        "readonly PATCH=\"$$(readlink -f $(location @patch))\"",
-        "readonly FILE=\"$$(readlink -f $(location @//debian:python_shapely_patches))\"",
-        "(cd $(@D) && \"$${PATCH}\" -p1 < \"$${FILE}\") > /dev/null",
-    ]),
-    tools = [
-        "@patch",
-    ],
-)
-
-genrule(
-    name = "patch_geos",
-    srcs = [
-        "usr/lib/python3/dist-packages/shapely/geos.py",
-        "@//debian:python_geos_patches",
-    ],
-    outs = ["shapely/geos.py"],
-    cmd = " && ".join([
-        "cp $(location usr/lib/python3/dist-packages/shapely/geos.py) $@",
-        "readonly PATCH=\"$$(readlink -f $(location @patch))\"",
-        "readonly FILE=\"$$(readlink -f $(location @//debian:python_geos_patches))\"",
-        "(cd $(@D) && \"$${PATCH}\" -p1 < \"$${FILE}\") > /dev/null",
-    ]),
-    tools = [
-        "@patch",
-    ],
-)
-
-_src_files = glob(
-    include = ["usr/lib/python3/dist-packages/**/*.py"],
-    exclude = [
-        "usr/lib/python3/dist-packages/gi/__init__.py",
-        "usr/lib/python3/dist-packages/shapely/__init__.py",
-        "usr/lib/python3/dist-packages/shapely/geos.py",
-    ],
-)
-
-_data_files = glob([
-    "usr/lib/x86_64-linux-gnu/girepository-1.0/**/*",
-    "usr/share/font*/**",
-    "etc/**",
-])
-
-_src_copied = ["/".join(f.split("/")[4:]) for f in _src_files]
-
-_builtin_so_files = glob([
-    "usr/lib/python3/dist-packages/**/*.cpython-39-x86_64-linux-gnu.so",
-])
-
-_system_so_files = glob(
-    include = [
-        "lib/x86_64-linux-gnu/**/*.so*",
-        "usr/lib/**/*.so*",
-    ],
-    exclude = [
-        "usr/lib/**/*.cpython-39-x86_64-linux-gnu.so",
-        "usr/lib/gcc/x86_64-linux-gnu/6/libgcc_s.so.1",
-        "usr/lib/gcc/x86_64-linux-gnu/6/libgcc_s.so",
-    ],
-)
-
-_builtin_so_copied = ["/".join(f.split("/")[4:]) for f in _builtin_so_files]
-
-_system_so_copied = ["rpathed/" + f for f in _system_so_files]
-
-_builtin_rpaths = [":".join([
-    "\\$$ORIGIN/%s" % rel,
-    "\\$$ORIGIN/%s/rpathed/usr/lib/x86_64-linux-gnu" % rel,
-    "\\$$ORIGIN/%s/rpathed/usr/lib" % rel,
-    "\\$$ORIGIN/%s/rpathed/lib/x86_64-linux-gnu" % rel,
-]) for rel in ["/".join([".." for _ in so.split("/")[1:]]) for so in _builtin_so_copied]]
-
-_system_rpaths = [":".join([
-    "\\$$ORIGIN/%s/rpathed/usr/lib/x86_64-linux-gnu" % rel,
-    "\\$$ORIGIN/%s/rpathed/usr/lib" % rel,
-    "\\$$ORIGIN/%s/rpathed/lib/x86_64-linux-gnu" % rel,
-]) for rel in ["/".join([".." for _ in so.split("/")[1:]]) for so in _system_so_copied]]
-
-genrule(
-    name = "run_patchelf_builtin",
-    srcs = _builtin_so_files,
-    outs = _builtin_so_copied,
-    cmd = "\n".join(
-        [
-            "cp $(location %s) $(location %s)" % (src, dest)
-            for src, dest in zip(_builtin_so_files, _builtin_so_copied)
-        ] +
-        ["$(location @patchelf) --set-rpath %s $(location %s)" % (rpath, so) for rpath, so in zip(_builtin_rpaths, _builtin_so_copied)],
-    ),
-    tools = [
-        "@patchelf",
-    ],
-)
-
-genrule(
-    name = "run_patchelf_system",
-    srcs = _system_so_files,
-    outs = _system_so_copied,
-    cmd = "\n".join(
-        [
-            "cp $(location %s) $(location %s)" % (src, dest)
-            for src, dest in zip(_system_so_files, _system_so_copied)
-        ] +
-        ["$(location @patchelf) --set-rpath %s $(location %s)" % (rpath, so) for rpath, so in zip(_system_rpaths, _system_so_copied)],
-    ),
-    tools = [
-        "@patchelf",
-    ],
-)
-
-genrule(
-    name = "copy_libgeos_c",
-    srcs = ["rpathed/usr/lib/x86_64-linux-gnu/libgeos_c.so.1"],
-    outs = ["rpathed/usr/lib/x86_64-linux-gnu/libgeos_c.so"],
-    cmd = "cp $< $@",
-)
-
-genrule(
-    name = "copy_files",
-    srcs = _src_files,
-    outs = _src_copied,
-    cmd = " && ".join(["cp $(location %s) $(location %s)" % (src, dest) for src, dest in zip(
-        _src_files,
-        _src_copied,
-    )]),
-)
-
-py_library(
-    name = "python_gtk",
-    srcs = _src_copied + [
-        "gi/__init__.py",
-        "shapely/__init__.py",
-        "shapely/geos.py",
-    ],
-    data = _data_files + _builtin_so_copied + _system_so_copied + [
-        "rpathed/usr/lib/x86_64-linux-gnu/libgeos_c.so",
-        "usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders",
-    ],
-    imports = ["usr/lib/python3/dist-packages"],
-    target_compatible_with = [
-        "@platforms//cpu:x86_64",
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-)
diff --git a/debian/python_gtk.bzl b/debian/python_gtk.bzl
deleted file mode 100644
index d542731..0000000
--- a/debian/python_gtk.bzl
+++ /dev/null
@@ -1,273 +0,0 @@
-files = {
-    "adwaita-icon-theme_3.38.0-1_all.deb": "2046876c82fc1c342b38ace9aa0661bcb3e167837c984b4bdc89702bc78df5ac",
-    "coreutils_8.32-4+b1_amd64.deb": "3558a412ab51eee4b60641327cb145bb91415f127769823b68f9335585b308d4",
-    "dconf-gsettings-backend_0.38.0-2_amd64.deb": "194991ed5f4ab1ca25413858cb99c910391cfd6d3b1b6a3d3e56a4b3a706a37d",
-    "dconf-service_0.38.0-2_amd64.deb": "639125f7a44d11f96661c61a07abbb58da0e5636ed406ac186adcef8651775c2",
-    "fontconfig-config_2.13.1-4.2_all.deb": "48afb6ad7d15e6104a343b789f73697301ad8bff77b69927bc998f5a409d8e90",
-    "fontconfig_2.13.1-4.2_amd64.deb": "c594a100759ef7c94149359cf4d2da5fb59ef30474c7a2dde1e049d32b9c478a",
-    "fonts-croscore_20201225-1_all.deb": "64904820b729ff40038f85683004e3b94b328d969bc0fbba263c58d635452923",
-    "fonts-dejavu-core_2.37-2_all.deb": "1f67421437b6eb18669d2868e3e02cb88668683d635198142f48aacc5b397118",
-    "fonts-freefont-otf_20120503-10_all.deb": "0b63996c80c6c660424af6d3832818e647960d6f65a51de010bb57dd0762faa7",
-    "fonts-freefont-ttf_20120503-10_all.deb": "4ca1c21ebc479198a3a5879d236c8317d6f7b2f1c403f7890e24c02eead05615",
-    "fonts-liberation2_2.1.3-1_all.deb": "e0805f0085132f5e6dd30f88c0d7260caf1e5450832fe2e3988a20fa9fa2150e",
-    "fonts-liberation_1.07.4-11_all.deb": "efd381517f958b01969343634ffcbdd60056be7779af84c6f53a005090430204",
-    "fonts-texgyre_20180621-3.1_all.deb": "cb7e9a4b2471cfdd57194c16364f9102f0639816a2662fed4b30d2a158747076",
-    "fonts-urw-base35_20200910-1_all.deb": "f95a139adb7f1b60626e76d4d45d1b35aad1bc2c2597394c291ef5f84b5dcb43",
-    "gir1.2-atk-1.0_2.36.0-2_amd64.deb": "36154c1e50e8e8013a14ce4ecfa1cf7527250beb49a2c60ac02ab2c8a40a5357",
-    "gir1.2-atspi-2.0_2.38.0-4_amd64.deb": "2b6f6d4c3de060e4f52cb7edba4c6ed9ab8d3601c4be617feac12e042df873ca",
-    "gir1.2-freedesktop_1.66.1-1+b1_amd64.deb": "60d8f35f0d67548088525543e3ff9e00934ebb5bfe7639afa45e5740e024f991",
-    "gir1.2-gdkpixbuf-2.0_2.42.2+dfsg-1_amd64.deb": "e21a68801467524241b65deda2be2418534e495ccc5ca8aad08405dd4ed03d46",
-    "gir1.2-glib-2.0_1.66.1-1+b1_amd64.deb": "1163a4e7eb095e37752739c0065bad50fa2177c13a87e7c1b0d44ed517fe8c91",
-    "gir1.2-gtk-3.0_3.24.24-4_amd64.deb": "1071a27b6ff7a786c5378dd5bb9792cf5c6e5f85808002919c0ec42db7c21154",
-    "gir1.2-harfbuzz-0.0_2.7.4-1_amd64.deb": "057b61d69437910e0350076cc0dd46d3ddb01ba181a434802aa328e81bc440d1",
-    "gir1.2-pango-1.0_1.46.2-3_amd64.deb": "0859356937e4b269201341ce410c77761fb68537ed3c317c223e7e67105ab0bb",
-    "glib-networking-common_2.66.0-2_all.deb": "a07370151ce5169e48ee7799b9bd9a7a035467a21f5cf3373b2aff090968609c",
-    "glib-networking-services_2.66.0-2_amd64.deb": "19131c7c31bc3fae604df30d2f73c3e8338ffeb2988fe167bb8b2b1c8913c9ab",
-    "glib-networking_2.66.0-2_amd64.deb": "b2cd50a8c3b30c16fd1a19c5244f681c6c0d1f426c385d44900477b052f70024",
-    "gsettings-desktop-schemas_3.38.0-2_all.deb": "3758968491a770e50cd85122c00d141944ffb43cb7a5c886a37290fef848cee3",
-    "gtk-update-icon-cache_3.24.24-4_amd64.deb": "612f35dd38e0d423560a7ad432fca0d2873db11334db2994ca5315e1e7984875",
-    "hicolor-icon-theme_0.17-2_all.deb": "20304d34b85a734ec1e4830badf3a3a70a5dc5f9c1afc0b2230ecd760c81b5e0",
-    "icu-devtools_67.1-7_amd64.deb": "0a89d6f360d9c686c08d0156a0c8244715c9aaeffca079cf1716f12cffece82e",
-    "libatk-bridge2.0-0_2.38.0-1_amd64.deb": "65b063b4b45c5fd60d91e374d01bb73eacdb30c545a6ef0873d07d6da97765d1",
-    "libatk-bridge2.0-dev_2.38.0-1_amd64.deb": "04be11ea79e542a4eea20977e23557c5cc21427e93c354d69b86586f81d248c7",
-    "libatk1.0-0_2.36.0-2_amd64.deb": "572cd62f92ec25c75b98617321373d46a6717cbcc93d2025ebd6d550f1abf901",
-    "libatk1.0-data_2.36.0-2_all.deb": "86c1acae473977f8a78b905090847df654306996324493f9a39d9f27807778b2",
-    "libatk1.0-dev_2.36.0-2_amd64.deb": "8a107ce46427f5cf68076eb0ab7e9b09f0237cb2674499da582c5a29cfc94d72",
-    "libatspi2.0-0_2.38.0-4_amd64.deb": "53435278eb8815aafbb41db29a691a43a9de16fa58d9bc7908a1f6f2a07f0b67",
-    "libatspi2.0-dev_2.38.0-4_amd64.deb": "fbbb10ba97dbfc79c5c1edc223e606c792332a47d242b21b1dea5c9bae5dbc2c",
-    "libattr1_2.4.48-6_amd64.deb": "af3c3562eb2802481a2b9558df1b389f3c6d9b1bf3b4219e000e05131372ebaf",
-    "libavahi-client3_0.8-5_amd64.deb": "697dff4185adc2912ee2b27c91bfb4fece4376dde2158dc7249a69498e4c0db0",
-    "libavahi-common-data_0.8-5_amd64.deb": "37595c0c6876ac914f66b081063a8522fb255afadb76e5613343a1d653beca0d",
-    "libavahi-common3_0.8-5_amd64.deb": "1300d89d5fb920753aee4c2b47b1ab1ef60533abe9875ba203096738f4cfb692",
-    "libblas3_3.9.0-3_amd64.deb": "489238f1d2f65dad98d134e5d7fec2a857422d7d2c8af029fc277cff0eec92d7",
-    "libblkid-dev_2.36.1-8+deb11u1_amd64.deb": "3f224b3dc4d094367b45b31c4bc367dd9528f45eba22af77229a7f9be7e6005d",
-    "libblkid1_2.36.1-8+deb11u1_amd64.deb": "9026ddd9f211008531ce6024d5ce042c723e237ecadfbf1f9343cb44aff492b9",
-    "libbrotli-dev_1.0.9-2+b2_amd64.deb": "520ef8f3af1a190ac2ce5954c0e42c8e6b80a593124f97e813be33e9e068ffc3",
-    "libbrotli1_1.0.9-2+b2_amd64.deb": "65ca7d8b03e9dac09c5d544a89dd52d1aeb74f6a19583d32e4ff5f0c77624c24",
-    "libbsd0_0.11.3-1_amd64.deb": "284a7b8dcfcad74770f57360721365317448b38ab773db542bf630e94e60c13e",
-    "libcairo-gobject2_1.16.0-5_amd64.deb": "a046d3ca805d4151029941fae736bfdf1c6f3dbcf1bd581102bd5ad844ea013e",
-    "libcairo-script-interpreter2_1.16.0-5_amd64.deb": "c1c47955283d36ccadbdfd88eef515063d28fdc20c751d70c863b18ca190ec8a",
-    "libcairo2-dev_1.16.0-5_amd64.deb": "a8ba01e9d19a1a4f512e7fa1ba1c089e2ace1a5b08733e167b9bea3fe86766de",
-    "libcairo2_1.16.0-5_amd64.deb": "b27210c0cf7757120e871abeba7de12a5cf94727a2360ecca5eb8e50ca809d12",
-    "libcolord2_1.4.5-3_amd64.deb": "b7f0b90535a04f25f4fe8a838b548eed87447b3225414bd4f30755ee917698dd",
-    "libcups2_2.3.3op2-3+deb11u1_amd64.deb": "b9545555975d3560612a44b23c362a03be517a75ddfa7a63bf828e03c57be37c",
-    "libdatrie-dev_0.2.13-1_amd64.deb": "0885c9b6c0a448b1faaa5fa51f3b751b986f33e48ca98ae901413c22a4a6e5a3",
-    "libdatrie1_0.2.13-1_amd64.deb": "3544f2cf26039fade9c7e7297dde1458b8386442c3b0fc26fdf10127433341c1",
-    "libdbus-1-3_1.12.20-2_amd64.deb": "7256dfeda88461e6fccbf98372d3ec29487b3b2d0ae5d145a3332ab35274f0da",
-    "libdbus-1-dev_1.12.20-2_amd64.deb": "0bf0161cb23cf6d3adb3b7d5b701b982a65ad1ecff21e6267e69d803b1d88108",
-    "libdconf1_0.38.0-2_amd64.deb": "ff3b1d05466782acd6e335b001460b7af4ea76f49bbbbd5447535d2b702fa97e",
-    "libdeflate0_1.7-1_amd64.deb": "dadaf0d28360f6eb21ad389b2e0f12f8709c9de539b28de9c11d7ec7043dec95",
-    "libdpkg-perl_1.20.9_all.deb": "134bd00e60fa30d39d5f676d306d6f1d61c7f6ec6086c1785dbc355ce6190f29",
-    "libdrm-amdgpu1_2.4.104-1_amd64.deb": "0005f21e342925bd26a25185289ae035aa931ced8f6fd9e3d4deade36d272ecd",
-    "libdrm-common_2.4.104-1_all.deb": "60c69026fb8e4cfdf8d80a4a86ee30516c611dcc4de4aa1c8ccbf06dff563e2b",
-    "libdrm-intel1_2.4.104-1_amd64.deb": "7d376adc7b5d4d83ec8414ff67dbc18765c6d420de9a6e1045fead7f1f82331d",
-    "libdrm-nouveau2_2.4.104-1_amd64.deb": "dbf4a3be55c609b1a2ea89d6782ae5c9a5b991844917dcd42c01666b73a96ceb",
-    "libdrm-radeon1_2.4.104-1_amd64.deb": "c33cd14e8ed7e2dfc02696ed51d4795c5797b0821666667e0a889bba705862b0",
-    "libdrm2_2.4.104-1_amd64.deb": "113396b3a33000f7f3347cd711ad9bcfe9945927331cc6cee63c751a889a967b",
-    "libedit2_3.1-20191231-2+b1_amd64.deb": "ac545f6ad10ba791aca24b09255ad1d6d943e6bc7c5511d5998e104aee51c943",
-    "libegl-dev_1.3.2-1_amd64.deb": "2847662b23487d5b1e467bca8cc8753baa880f794744a9b492c978bd5514b286",
-    "libegl-mesa0_20.3.5-1_amd64.deb": "a0c36a3665af89cbc96f865bd1b64c6c07b93096e91ba5b470d375d02dfa6d82",
-    "libegl1-mesa-dev_20.3.5-1_amd64.deb": "7b8139acb2e43a50fd952d54b41449baf13b404f65dccf187ae7852f028104f9",
-    "libegl1_1.3.2-1_amd64.deb": "3a5583ebd7a9d8ad102484db9637c409561428d21345037b310c4ef2ca8e8837",
-    "libelf1_0.183-1_amd64.deb": "e1ad132d502b255023c222d0cae1d02ca941f6b68fd0e9b908c6004cc326592c",
-    "libepoxy-dev_1.5.5-1_amd64.deb": "3979a7f81ffe10efcb1dcc3bd6e3ced5a88d1fe0ed68b12fb4cc4133b3e3d1b1",
-    "libepoxy0_1.5.5-1_amd64.deb": "3d050c9b138872c83b5b3521c97ab89f8a885b1391fdd0477cf8168ae54728a3",
-    "libffi-dev_3.3-6_amd64.deb": "ca2c71d9c68b1944b689606f12acf8023bad1b5083e8413894fd41ad0b977d20",
-    "libfontconfig-dev_2.13.1-4.2_amd64.deb": "7655d4238ee7e6ced13501006d20986cbf9ff08454a4e502d5aa399f83e28876",
-    "libfontconfig1-dev_2.13.1-4.2_amd64.deb": "a19502912fb57c1e9c87efbd7b7adad7f1c1b793164580ddf02168f0cfec59fb",
-    "libfontconfig1_2.13.1-4.2_amd64.deb": "b92861827627a76e74d6f447a5577d039ef2f95da18af1f29aa98fb96baea4c1",
-    "libfreetype-dev_2.10.4+dfsg-1_amd64.deb": "9f40a4e50a8bd56e4d78335876de1e0a28118dc8da909c3845bb6ef9a079d30d",
-    "libfreetype6-dev_2.10.4+dfsg-1_amd64.deb": "b7cae5a368eaf87a0f426b43f7b9079a7643b42ed05c7818a4868ca0fdd16a16",
-    "libfreetype6_2.10.4+dfsg-1_amd64.deb": "e95396fc3cc806b2b95d9a00b4226eb464bc3ef4817c798749a0dd582546e5bc",
-    "libfribidi-dev_1.0.8-2_amd64.deb": "91b84f6ec2e4a4f367f974155c0ff59efc166ae2ee80293e855009e9f675c7dd",
-    "libfribidi0_1.0.8-2_amd64.deb": "fa4c6ea0d4d4709b2414a9d9567a3f9d35cd8a270c8dcc8bd79d046fc200b914",
-    "libgbm1_20.3.5-1_amd64.deb": "2d9b07282e46e3c9398613b6d4fe86c3259e4326b158be7e1f4f58cab541156c",
-    "libgcrypt20_1.8.7-6_amd64.deb": "7a2e0eef8e0c37f03f3a5fcf7102a2e3dc70ba987f696ab71949f9abf36f35ef",
-    "libgdk-pixbuf-2.0-0_2.42.2+dfsg-1_amd64.deb": "2dd0745a0dde7f6afb97a8ea0a30ce266c34d4f11b023e096437a8cd862f4595",
-    "libgdk-pixbuf-2.0-dev_2.42.2+dfsg-1_amd64.deb": "cfcb5e38b748e68bdb744dad469e7812a7c2f08b0c8263dd0280c97161d271b9",
-    "libgdk-pixbuf-xlib-2.0-0_2.40.2-2_amd64.deb": "c11e9c92534e1e8036ad33a7ee1962b120834a02c41594cdf90ce01855ba84a4",
-    "libgdk-pixbuf-xlib-2.0-dev_2.40.2-2_amd64.deb": "5769f16c81c72ce50bd2aa8a7724b511d84ab411f9f23d5ef79b40fbd59c57e9",
-    "libgdk-pixbuf2.0-bin_2.42.2+dfsg-1_amd64.deb": "02e3133e805e5225bc16be1d6be8b80daa47fec5c4987c5b87ec0a28d06f973c",
-    "libgdk-pixbuf2.0-common_2.42.2+dfsg-1_all.deb": "61ff764860dafbd7e3fe2050b9c17db3ae109dea15ac748212eff56fdb3111e1",
-    "libgdk-pixbuf2.0-dev_2.40.2-2_amd64.deb": "98d1fe8b2fc224569a04d18f4a0efd6d5482feb47f85b1f4f3e149972a44a93e",
-    "libgeos-3.9.0_3.9.0-1_amd64.deb": "c6190966a2410f01f14ce5265e362ba77fdf8c25f3b08b5af71ee05f8d70b09e",
-    "libgeos-c1v5_3.9.0-1_amd64.deb": "88072c56bf83ab01f97096fac8b8acdfdad7122c01dcb9fd9825ef4ac525d3fe",
-    "libgfortran5_10.2.1-6_amd64.deb": "6fe41d04ea9ef8c5c684b14585caa7a4a7e04ad6805d59cdd29016960b737123",
-    "libgirepository-1.0-1_1.66.1-1+b1_amd64.deb": "787e913bf56f19bc54720c3463ab8afe1cc9442536fde31e2a36afc3939f28c9",
-    "libgl-dev_1.3.2-1_amd64.deb": "a6487873f2706bbabf9346cdb190f47f23a1464f31cecf92c363bac37c342f2f",
-    "libgl1-mesa-dri_20.3.5-1_amd64.deb": "08e8bc20077e188da7061f77d23a336782d8463c0cc112fabbfa9c8b45923fd2",
-    "libgl1_1.3.2-1_amd64.deb": "f300f9610b5f05f1ce566c4095f1bf2170e512ac5d201c40d895b8fce29dec98",
-    "libglapi-mesa_20.3.5-1_amd64.deb": "aa8f8eaf13224cbb8729416be79350460f7f2230193b2da5d5e24f3dc7e9985f",
-    "libgles-dev_1.3.2-1_amd64.deb": "969e9197d8b8a36780f9b5d86f7c3066cdfef9dd7cdc3aee59a1870415c53578",
-    "libgles1_1.3.2-1_amd64.deb": "18425a2558be1de779c7c71ce780b133381f0db594a901839c6ae3d8e3f3c966",
-    "libgles2_1.3.2-1_amd64.deb": "367116f5e3b3a003a80203848b5ce1401451a67c2b2b9d6a383efc91badb0724",
-    "libglib2.0-0_2.66.8-1_amd64.deb": "995469490dcc8f667df8051a39dd5abd7149d849456c28af4e58cbfd6d6dc4f8",
-    "libglib2.0-bin_2.66.8-1_amd64.deb": "5adf4c916832ad4203fed68faacd4552361cbccc22f66f4504a7ad6fc955bddd",
-    "libglib2.0-data_2.66.8-1_all.deb": "be41a674336cefd00e2a468fe19c8bbf9f3fac86f39379e1b7acbad41f6af644",
-    "libglib2.0-dev-bin_2.66.8-1_amd64.deb": "2dbca7691d2b43545d7a89bafb4cc92a5e42c68085fa4d8989e74b1f5250f9c6",
-    "libglib2.0-dev_2.66.8-1_amd64.deb": "782fcfd549266048309b8da556377c16445bafe9f0aec31d9f246ac9b736d2aa",
-    "libglvnd-dev_1.3.2-1_amd64.deb": "e330ccbe6338789fd63212b55009dcce733265799395ad55b300cd1427234e7f",
-    "libglvnd0_1.3.2-1_amd64.deb": "52a4464d181949f5ed8f7e55cca67ba2739f019e93fcfa9d14e8d65efe98fffc",
-    "libglx-dev_1.3.2-1_amd64.deb": "5a50549948bc4363eab32b1083dad2165402c3628f2ee85e9a32563228cc61c1",
-    "libglx-mesa0_20.3.5-1_amd64.deb": "2d19e2addfbea965220e62f512318351f12bdfe7e180f265f00d0f2834a77833",
-    "libglx0_1.3.2-1_amd64.deb": "cb642200f7e28e6dbb4075110a0b441880eeec35c8a00a2198c59c53309e5e17",
-    "libgmp10_6.2.1+dfsg-1+deb11u1_amd64.deb": "fc117ccb084a98d25021f7e01e4dfedd414fa2118fdd1e27d2d801d7248aebbc",
-    "libgnutls30_3.7.1-5_amd64.deb": "20b0189b72ad4c791cf5b280c111d41ce071a04dab0e9a9d7daa9504a7a7b543",
-    "libgpg-error0_1.38-2_amd64.deb": "16a507fb20cc58b5a524a0dc254a9cb1df02e1ce758a2d8abde0bc4a3c9b7c26",
-    "libgraphite2-3_1.3.14-1_amd64.deb": "31113b9e20c89d3b923da0540d6f30535b8d14f32e5904de89e34537fa87d59a",
-    "libgraphite2-dev_1.3.14-1_amd64.deb": "aa0437ff7c38b6e68a0bbcc3f18163677372e99fe3ec9673a552d8a8521aba64",
-    "libgtk-3-0_3.24.24-4_amd64.deb": "264b629191b03bf239da61c4995b03abc9e039f585d9954f28230db67e345d5e",
-    "libgtk-3-common_3.24.24-4_all.deb": "c7ce143bed115bc868976538089dc15c0c469ea67cbf84ab412e55d95ee5b488",
-    "libgtk-3-dev_3.24.24-4_amd64.deb": "5ddc4b46b0490b52c68825ecd40084e468dfcbc179c518794090e185c08e8dd3",
-    "libharfbuzz-dev_2.7.4-1_amd64.deb": "e2b5b9331990dc71da22fe05529cc72220e5449bcb98b08338c00e2f697cca65",
-    "libharfbuzz-gobject0_2.7.4-1_amd64.deb": "3c3cbf4150275173e7b4cdb0b12b8670867e70c27c0a31fd6559f4ce68a7dd84",
-    "libharfbuzz-icu0_2.7.4-1_amd64.deb": "43b41efde4c41c04b067f1d2917f33cb2d6a56b8e5d770e53ee71d7debdd241b",
-    "libharfbuzz0b_2.7.4-1_amd64.deb": "c76825341b5877240ff2511a376844a50ffda19d9d019ae65a5b3a97f9a1a183",
-    "libhogweed6_3.7.3-1_amd64.deb": "6aab2e892cdb2dfba45707601bc6c3b19aa228f70ae5841017f14c3b0ca3d22f",
-    "libice-dev_1.0.10-1_amd64.deb": "9d111d7e07104f7b9cc284d32e811ab01f376613f163b0580fbd7b61440ff669",
-    "libice6_1.0.10-1_amd64.deb": "452796e565c9d42386bd59990000ae9c37d85e142e00ee2b14df0787e2bbf970",
-    "libicu-dev_67.1-7_amd64.deb": "7932a6acfbfd76e1dbedcf171dafda9e549b8dc179a666043dbb3d5b733c4a29",
-    "libicu67_67.1-7_amd64.deb": "2bf5c46254f527865bfd6368e1120908755fa57d83634bd7d316c9b3cfd57303",
-    "libidn2-0_2.3.0-5_amd64.deb": "cb80cd769171537bafbb4a16c12ec427065795946b3415781bc9792e92d60b59",
-    "libjbig0_2.1-3.1+b2_amd64.deb": "9646d69eefce505407bf0437ea12fb7c2d47a3fd4434720ba46b642b6dcfd80f",
-    "libjpeg62-turbo_2.0.6-4_amd64.deb": "28de780a1605cf501c3a4ebf3e588f5110e814b208548748ab064100c32202ea",
-    "libjson-glib-1.0-0_1.6.2-1_amd64.deb": "c2db69dda6ceda43065d694c5ebd515900dd38d7231a74016f10a2d2a870f01d",
-    "libjson-glib-1.0-common_1.6.2-1_all.deb": "a938ec35a20dca2e5878a8750fb44683b67a5f7c2d23d383963803a9fcfac1a3",
-    "liblapack3_3.9.0-3_amd64.deb": "7fc4cd55ca777dbe0745bd167abebed0b5d64b5cdff8900fec2ae579859fbade",
-    "liblcms2-2_2.12~rc1-2_amd64.deb": "0608ecb6ed258814e390b52b3fb50f2a6d3239b5ecb1086292ae08be00a67b0f",
-    "libllvm11_11.0.1-2_amd64.deb": "eaff3c8dd6039af90b8b6bdbf33433e35d8c808a7aa195d0e3800ef5e61affff",
-    "liblz4-1_1.9.3-2_amd64.deb": "79ac6e9ca19c483f2e8effcc3401d723dd9dbb3a4ae324714de802adb21a8117",
-    "liblzo2-2_2.10-2_amd64.deb": "4f08e092c76e425295a498cd547dc9b8f6a595473f3020ab8c96309b29872636",
-    "libmd0_1.0.3-3_amd64.deb": "9e425b3c128b69126d95e61998e1b5ef74e862dd1fc953d91eebcc315aea62ea",
-    "libmount-dev_2.36.1-8+deb11u1_amd64.deb": "e2ab59f02398ff5f50d58ba5702a3dc27d47b6b028fccab03d0e8060e317f328",
-    "libmount1_2.36.1-8+deb11u1_amd64.deb": "a3d8673804f32e9716e33111714e250b6f1092770a52e21fab99d0ab4b48c5d9",
-    "libnettle8_3.7.3-1_amd64.deb": "e4f8ec31ed14518b241eb7b423ad5ed3f4a4e8ac50aae72c9fd475c569582764",
-    "libopengl-dev_1.3.2-1_amd64.deb": "7e598e73830ffb5d6fae58ebd1c769b6f7806dc92bd5649893b74f1302b47e82",
-    "libopengl0_1.3.2-1_amd64.deb": "4327a9f20b88e7bcb07af3b196121096877331b61eeed64467854eb0b525fc43",
-    "libp11-kit0_0.23.22-1_amd64.deb": "bfef5f31ee1c730e56e16bb62cc5ff8372185106c75bf1ed1756c96703019457",
-    "libpango-1.0-0_1.46.2-3_amd64.deb": "cfb3079a7397cc7d50eabe28ea70ce15ba371c84efafd8f8529ee047e667f523",
-    "libpango1.0-dev_1.46.2-3_amd64.deb": "5da5a8009ab6275b12193e277bf6d091b29203f058d246d5b8a184d1c7f0cde6",
-    "libpangocairo-1.0-0_1.46.2-3_amd64.deb": "f0489372e4bcb153d750934eb3cddd9104bc3a46d564aa10bef320ba89681d37",
-    "libpangoft2-1.0-0_1.46.2-3_amd64.deb": "78067d7222459902e22da6b4c1ab8ee84940752d25a5f3dea1a43f846a8562e3",
-    "libpangoxft-1.0-0_1.46.2-3_amd64.deb": "621545808843e84288039a55df16023ff872f48b3a155788dba7a1cea25c7a9b",
-    "libpciaccess0_0.16-1_amd64.deb": "f581ced157bd475477337860e7e7fcabeeb091444bc5a189c5c97adc8fcabda5",
-    "libpcre16-3_8.39-13_amd64.deb": "04ef146b0119a8a5ab1df09d990bd61a45bf99d2989aa248ebc7f72dbb99544e",
-    "libpcre2-16-0_10.36-2_amd64.deb": "720aa56730b7916680ce2859dbdaa722aa519859b0697d78b34e5c57ee6293c2",
-    "libpcre2-32-0_10.36-2_amd64.deb": "89558554df9e374de506d8372341e1a45a0d6ea8413dc2e49d5d357e571555ee",
-    "libpcre2-dev_10.36-2_amd64.deb": "75de539e873d7c58805ab38a4e17a7fb434abde8beadbe6fe4b8e477e84d68e5",
-    "libpcre2-posix2_10.36-2_amd64.deb": "179664cb063e1761fc8ebe04f8a02f17be22b79b1bdcf66404c3ee35b3884d09",
-    "libpcre3-dev_8.39-13_amd64.deb": "e588a2bd07e2770ad2fa9e3b02e359d3ff3c6f0c17a809365d3e97da7b0e64e0",
-    "libpcre32-3_8.39-13_amd64.deb": "961135f3ff2d00c2e46640b9730d9ddef80ae9d9037e2ec882ee8f6ce5dd48c9",
-    "libpcre3_8.39-13_amd64.deb": "48efcf2348967c211cd9408539edf7ec3fa9d800b33041f6511ccaecc1ffa9d0",
-    "libpcrecpp0v5_8.39-13_amd64.deb": "79e15b8d31f8561ad1c19f8c280d0a9fe280f7872701ef53c9bdfce6b3015a18",
-    "libpixman-1-0_0.40.0-1_amd64.deb": "55236a7d4b9db107eb480ac56b3aa786572ea577ba34323baf46aceb7ba6d012",
-    "libpixman-1-dev_0.40.0-1_amd64.deb": "bcde62aee0fe759798e8a4d3a3d9b0666ba5ab15d1cb9e69fa000ff23ba305cb",
-    "libproxy1v5_0.4.17-1_amd64.deb": "b21c1524b972dd72387ecb8b12c0a860738ce0832ed18fe7ffb9da6adc9b9e41",
-    "libpsl5_0.21.0-1.2_amd64.deb": "d716f5b4346ec85bb728f4530abeb1da4a79f696c72d7f774c59ba127c202fa7",
-    "libpthread-stubs0-dev_0.4-1_amd64.deb": "54632f160e1e8a43656a87195a547391038c4ca0f53291b849cd4457ba5dfde9",
-    "libquadmath0_10.2.1-6_amd64.deb": "a9a5e1f53b7e27a3f2b8388929bb622d3c6c35a4e42ac166697444e5ed662fd5",
-    "librest-0.7-0_0.8.1-1.1_amd64.deb": "5cd57a96145a362bf60428315ab3fc6c2f528ab38a06a905da2568575c23bdc8",
-    "libselinux1-dev_3.1-3_amd64.deb": "16b14d7e8ed88b9b07d1b52d84d04ab2fcdfcdc4b8cecc9dd34df06f3ce7d3fb",
-    "libsensors-config_3.6.0-7_all.deb": "4265811140a591d27c99d026b63707d8235d98c73d7543c66ab9ec73c28523fc",
-    "libsensors5_3.6.0-7_amd64.deb": "b9cb9a081ea3c9b68ef047d7e51f3b84bccde1a2467d5657df4c5d54775b187e",
-    "libsepol1-dev_3.1-1_amd64.deb": "1bec586de489db87c8746a6eeed27982915fc578c91e9e78ef39773ab824e023",
-    "libsepol1_3.1-1_amd64.deb": "b6057dc6806a6dfaef74b09d84d1f18716d7a6d2f1da30520cef555210c6af62",
-    "libsm-dev_1.2.3-1_amd64.deb": "2ff8641d3217dc1a0f26514f5d8de2009669423a4aa0db46b3df564a8b367026",
-    "libsm6_1.2.3-1_amd64.deb": "22a420890489023346f30fecef14ea900a0788e7bf959ef826aabb83944fccfb",
-    "libsoup-gnome2.4-1_2.72.0-2_amd64.deb": "7fdc774b567e3a5e0881aa01fcfcac637fdeeb8ea6233b710571e1f5b3a994b6",
-    "libsoup2.4-1_2.72.0-2_amd64.deb": "32dad5305be0faa619df36688a20d187ba915f02e9e184cc5c3c6e3d98259e9c",
-    "libsystemd0_247.3-6_amd64.deb": "8c948d9d97178e6617f549822db2b89e23b1bfa1ee745ffbf0e41b6ee64f8737",
-    "libtasn1-6_4.16.0-2_amd64.deb": "fd7a200100298c2556e67bdc1a5faf5cf21c3136fa47f381d7e9769233ee88a1",
-    "libthai-data_0.1.28-3_all.deb": "64750cb822e54627a25b5a00cde06e233b5dea28571690215f672af97937f01b",
-    "libthai-dev_0.1.28-3_amd64.deb": "b633b5fbe6220f69fe78019817a3176124e64c5e402cf1142bac14ec93bfbb4b",
-    "libthai0_0.1.28-3_amd64.deb": "446e2b6e8e8a0f5f6c0de0a40c2aa4e1c2cf806efc450c37f5358c7ff1092d6a",
-    "libtiff5_4.2.0-1_amd64.deb": "ec12fc6a05a30f8f98878dbb6d9b356d170d5f6239fb68ba6a1dc9eb89ba3194",
-    "libudev1_247.3-6_amd64.deb": "b9a530020ef3fa141500f64b9930cb51902eab3dfc8a653b7e9fa8cd1bc7c863",
-    "libunistring2_0.9.10-4_amd64.deb": "654433ad02d3a8b05c1683c6c29a224500bf343039c34dcec4e5e9515345e3d4",
-    "libvulkan1_1.2.162.0-1_amd64.deb": "8b3a6e5db7d8bdc369a0d276bfae1551ffc0fa31dbd193d56655c8f553868361",
-    "libwayland-bin_1.18.0-2~exp1.1_amd64.deb": "774e97053d524549044b332469d13eec70c989b4bc00a592019512c17a92978e",
-    "libwayland-client0_1.18.0-2~exp1.1_amd64.deb": "4baf16bb3a35823251453368ee078b6be6a14f97b05c19783b5acd4232a608ea",
-    "libwayland-cursor0_1.18.0-2~exp1.1_amd64.deb": "1b48d1d8e17a95b28a2876c7f2a95667ee1618a5f586d4dff05aeb09488172cb",
-    "libwayland-dev_1.18.0-2~exp1.1_amd64.deb": "3265bf05c0cea760d0e8f5fb5fc68b0f154911de23503e02232dfa59f6b6490c",
-    "libwayland-egl1_1.18.0-2~exp1.1_amd64.deb": "b98e636f08eca9e818e326fc8cd75810dbb50b1ed4e3586c2394e11248e29275",
-    "libwayland-server0_1.18.0-2~exp1.1_amd64.deb": "1df9a6e304bdaebdd53e1044c6eadcda95c914119e9426c2866eaa619a49c85b",
-    "libwebp6_0.6.1-2.1_amd64.deb": "52bfd0f8d3a1bbd2c25fcd72fab857d0f24aea35874af68e057dde869ae3902c",
-    "libx11-6_1.7.2-1_amd64.deb": "086bd667fc07369472a923da015d182bb0c15a72228a5c0e6ddbcbeaab70acd2",
-    "libx11-data_1.7.2-1_all.deb": "049b7eabced516acfdf44a5e81c26d108b16e4987e5d7604ea53eaade74027fb",
-    "libx11-dev_1.7.2-1_amd64.deb": "11e5f9dcded1a1226b3ee02847b86edce525240367b3989274a891a43dc49f5f",
-    "libx11-xcb1_1.7.2-1_amd64.deb": "1f9f2dbe7744a2bb7f855d819f43167df095fe7d5291546bec12865aed045e0c",
-    "libxau-dev_1.0.9-1_amd64.deb": "d1a7f5d484e0879b3b2e8d512894744505e53d078712ce65903fef2ecfd824bb",
-    "libxau6_1.0.9-1_amd64.deb": "679db1c4579ec7c61079adeaae8528adeb2e4bf5465baa6c56233b995d714750",
-    "libxcb-dri2-0_1.14-3_amd64.deb": "fbfc7d55fa00ab7068d015c185363370215c857ac9484d7020c2d9c38c8401b2",
-    "libxcb-dri3-0_1.14-3_amd64.deb": "4dd503b321253f210fe546aae8fe5061fc7d30015cf5580d7843432a71ebc772",
-    "libxcb-glx0_1.14-3_amd64.deb": "61ae35a71148038aad04b021b3adfa0dee4fc06d98e045ec9edfd9e850324876",
-    "libxcb-present0_1.14-3_amd64.deb": "7937af87426de2ed382ba0d6204fee58f4028b332625e2727ebb7ca9a1b32028",
-    "libxcb-render0-dev_1.14-3_amd64.deb": "f3335e206e938c760df5f933e35f370e850050e5c2c9ce0568f190970a6cac41",
-    "libxcb-render0_1.14-3_amd64.deb": "3d653df34e5cd35a78a9aff1d90c18ec0200e5574e27bc779315b855bea2ecc0",
-    "libxcb-shm0-dev_1.14-3_amd64.deb": "283d20ecde030b6905e7042f427a434a6334556a6475b11422278919f4c0c840",
-    "libxcb-shm0_1.14-3_amd64.deb": "0751b48b1c637b5b0cb080159c29b8dd83af8ec771a21c8cc26d180aaab0d351",
-    "libxcb-sync1_1.14-3_amd64.deb": "53e7f18c8a95b2be2024537a753b6bd914af5f4c7aeed175f61155a5a3c8fe88",
-    "libxcb-xfixes0_1.14-3_amd64.deb": "939b29a4eaad5972ba379c2b5f29cf51d7d947b10e68cc2fe96238efcd3d63c2",
-    "libxcb1-dev_1.14-3_amd64.deb": "b75544f334c8963b8b7b0e8a88f8a7cde95a714dddbcda076d4beb669a961b58",
-    "libxcb1_1.14-3_amd64.deb": "d5e0f047ed766f45eb7473947b70f9e8fddbe45ef22ecfd92ab712c0671a93ac",
-    "libxcomposite-dev_0.4.5-1_amd64.deb": "6aecea058e55f46341be898d6e21b933fee5a314e3133ec33b4b88441e7d52b4",
-    "libxcomposite1_0.4.5-1_amd64.deb": "4c26ebf519d2ebc22fc1416dee45e12c4c4ef68aa9b2ed890356830df42b652a",
-    "libxcursor-dev_1.2.0-2_amd64.deb": "c84c43ad4d596bd673288f6035ced4755468b873149181936a0c1fc99cff78aa",
-    "libxcursor1_1.2.0-2_amd64.deb": "d9fee761e4c50572c3ce3c3965b70fcfecd277d0d7d598e102134d12757a3d11",
-    "libxdamage-dev_1.1.5-2_amd64.deb": "34a580b62466411b34a0be2bb0d00c3ec268da96e80d0adc40b379c97e9bac37",
-    "libxdamage1_1.1.5-2_amd64.deb": "1acf6d6117929a7df346d355caeb579798d75feb7e3b3aae58a2d1af735b444f",
-    "libxdmcp-dev_1.1.2-3_amd64.deb": "c6733e5f6463afd261998e408be6eb37f24ce0a64b63bed50a87ddb18ebc1699",
-    "libxdmcp6_1.1.2-3_amd64.deb": "ecb8536f5fb34543b55bb9dc5f5b14c9dbb4150a7bddb3f2287b7cab6e9d25ef",
-    "libxext-dev_1.3.3-1.1_amd64.deb": "0aa17565287ca8a37914e043789ee33c6e1f987acf346dac7175165009c5db7c",
-    "libxext6_1.3.3-1.1_amd64.deb": "dc1ff8a2b60c7dd3c8917ffb9aa65ee6cda52648d9150608683c47319d1c0c8c",
-    "libxfixes-dev_5.0.3-2_amd64.deb": "bacfcd67ca931839a2e2ae87922ecb40e2870470afa86d0c7245288825da2340",
-    "libxfixes3_5.0.3-2_amd64.deb": "58622d0d65c3535bd724c4da62ae7acb71e0e8f527bcbd65daf8c97e6f0ef843",
-    "libxft-dev_2.3.2-2_amd64.deb": "c6919b13423d4e3b41b7650b2d9bb6f6deac2906b3d51d047b5898a169ebc13c",
-    "libxft2_2.3.2-2_amd64.deb": "cd71384b4d511cba69bcee29af326943c7ca12450765f44c40d246608c779aad",
-    "libxi-dev_1.7.10-1_amd64.deb": "736309ff476f0e1594f855cf44e2fb20bf1e594518ce2259eb9b2dd93917f2db",
-    "libxi6_1.7.10-1_amd64.deb": "4d583f43b5396ca5434100a7274613e9983357d80875a47b29a4f3218fe0bec0",
-    "libxinerama-dev_1.1.4-2_amd64.deb": "18047f52c3a1294d61bc2642d22d05bd879c15393c4e8b4ac2ee6a5061585b9b",
-    "libxinerama1_1.1.4-2_amd64.deb": "f692c854935571ee44fe313541d8a9f678a4f11dc513bc43b9d0a501c6dff0bd",
-    "libxkbcommon-dev_1.0.3-2_amd64.deb": "1202d8c64e670876b58f5b3d3f797b1848ec462f7caeef8b1e597ecea18570b6",
-    "libxkbcommon0_1.0.3-2_amd64.deb": "d74d0b9f0a6641b44c279644c7ac627fa7a9b92350b7c6ff37da94352885bcfc",
-    "libxml2_2.9.10+dfsg-6.7_amd64.deb": "023296a15e1a28607609cb15c7ca0dd8a25160f3e89a0da58368319c7e17d4e0",
-    "libxrandr-dev_1.5.1-1_amd64.deb": "67d40174015e8b4e3e2fe3b6fb2990943321a168e0fbb2d12082f637914a0a2e",
-    "libxrandr2_1.5.1-1_amd64.deb": "8fdd8ba4a8ad819731d6bbd903b52851a2ec2f9ef4139d880e9be421ea61338c",
-    "libxrender-dev_0.9.10-1_amd64.deb": "135ed7c8a589e17d21718a91b5a7da48159f33c85e0b337aae9b9f484d3a4954",
-    "libxrender1_0.9.10-1_amd64.deb": "3ea17d07b5aa89012130e2acd92f0fc0ea67314e2f5eab6e33930ef688f48294",
-    "libxshmfence1_1.3-1_amd64.deb": "1a38142e40e3d32dc4f9a326bf5617363b7d9b4bb762fdcdd262f2192092024d",
-    "libxtst-dev_1.2.3-1_amd64.deb": "9ed56e0fd5807afe20cfee8fad16c657c6d7410d7934d8726584794bd77ea989",
-    "libxtst6_1.2.3-1_amd64.deb": "7072f9be17abdb9c5af7d052b19c84d1a6c1c13c30c120a98d284ba73d2da73f",
-    "libxxf86vm1_1.1.4-1+b2_amd64.deb": "6f4ca916aaec26d7000fa7f58de3f71119309ab7590ce1f517abfe1825a676c7",
-    "libz3-4_4.8.10-1_amd64.deb": "7a38c2dd985eb9315857588ee06ff297e2b16de159dec85bd2777a43ebe9f458",
-    "libzstd1_1.4.8+dfsg-2.1_amd64.deb": "5dcadfbb743bfa1c1c773bff91c018f835e8e8c821d423d3836f3ab84773507b",
-    "lsb-base_11.1.0_all.deb": "89ed6332074d827a65305f9a51e591dff20641d61ff5e11f4e1822a9987e96fe",
-    "pango1.0-tools_1.46.2-3_amd64.deb": "e622e68a9451c9d15fd2a5c4c4a95884f33bdfece63f9c0d6cd3953c5d202e74",
-    "perl_5.32.1-4+deb11u2_amd64.deb": "1cebc4516ed7c240b812c7bdd7e6ea0810f513152717ca17ce139ee0dfbc7b0d",
-    "pkg-config_0.29.2-1_amd64.deb": "09a05a23c5fd5baacd488255a6b0114909210691b830fb951acd276e9bcd632a",
-    "python3-cairo_1.16.2-4+b2_amd64.deb": "e367c3a2f6755180bba2f99ac1876c0b550cefb4c53df3949963309be9a88bda",
-    "python3-gi-cairo_3.38.0-2_amd64.deb": "14ffefd284a995346fdac617d825c9f9b43435c991fe45089cf3eaa130eba5ee",
-    "python3-gi_3.38.0-2_amd64.deb": "48d4d4413013d4bb044feb6c8d9a470108d59546c09f4630187fda8164b8fda1",
-    "python3-numpy_1.19.5-1_amd64.deb": "fe8c8955be71be9f744729b73c73cc6bc01184daf253604f1d3ee16a8d64d30d",
-    "python3-pkg-resources_52.0.0-4_all.deb": "e81cb1b4a3aa739fa5bcfceb043c3bd47233b4f5a36f88ad2de1f05a83d6ec2b",
-    "python3-shapely_1.7.1-2_amd64.deb": "13e188f5b96a985f3f1ef77089a91c00ff8138aca6217f55514c0a8d4952c229",
-    "python3.9_3.9.2-1_amd64.deb": "12f8b47632cc26b986bfc4c882fc98e7036d406143ae8bb1dbfff6b61d7c8993",
-    "sensible-utils_0.0.14_all.deb": "b9a447dc4ec8714196b037e20a2209e62cd669f5450222952f259bda4416b71f",
-    "shared-mime-info_2.0-1_amd64.deb": "de0a814e186af5a941e1fcd3044da62eb155638fcf9616d6005bcfc6696bbe67",
-    "ttf-bitstream-vera_1.10-8.1_all.deb": "ba622edf73744b2951bbd20bfc113a1a875a9b0c6fed1ac9e9c7f4b54dd8a048",
-    "ucf_3.0043_all.deb": "ebef6bcd777b5c0cc2699926f2159db08433aed07c50cb321fd828b28c5e8d53",
-    "uuid-dev_2.36.1-8+deb11u1_amd64.deb": "90a533bbb3b82f5c9bedc5da28965ca8223913099f8ac67213e4f8828bfdd2a1",
-    "wayland-protocols_1.20-1_all.deb": "09bcb6b1d7735a0190ec85f73680dfd53cfa91ff139c8b3d4e18377f94bb6599",
-    "x11-common_7.7+22_all.deb": "5d1c3287826f60c3a82158b803b9c0489b8aad845ca23a53a982eba3dbb82aa3",
-    "x11proto-core-dev_2020.1-1_all.deb": "92941b1b2a7889a67e952a9301339202b6b390b77af939a26ee15c94ef4fad7e",
-    "x11proto-dev_2020.1-1_all.deb": "d5568d587d9ad2664c34c14b0ac538ccb3c567e126ee5291085a8de704a565f5",
-    "x11proto-input-dev_2020.1-1_all.deb": "f2b5dbb98ddafb56b3a6d4d5545812b98e272c146f79adb41e49533eeaa97d3f",
-    "x11proto-randr-dev_2020.1-1_all.deb": "ca63b15ebe65d1e45868d72eb87cd447be3adeb5cc25787db09f65ef05e30c66",
-    "x11proto-record-dev_2020.1-1_all.deb": "dc0ceb54206b03107d31d0ce8665d47ce7c7debac5c3e072e041cdc37f176d3f",
-    "x11proto-render-dev_2020.1-1_all.deb": "f622a9bdd90d51305cd92ee3c5d30ca82f1a20aea3632514965afed6e85589c7",
-    "x11proto-xext-dev_2020.1-1_all.deb": "61e858b8758b8ff63dfc8206d3b00bfbe3ad36ef133dea41b3c5b73dc427d41b",
-    "x11proto-xinerama-dev_2020.1-1_all.deb": "0183efc631edb1308b2bc38ae08f3dc27db735f8d1e84d87bde6416fa023c70d",
-    "xkb-data_2.29-2_all.deb": "9122cccc67e6b3c3aef2fa9c50ef9d793a12f951c76698a02b1f4ceb9e3634e5",
-    "xorg-sgml-doctools_1.11-1.1_all.deb": "168345058319094e475a87ace66f5fb6ae802109650ea8434d672117982b5d0a",
-    "xtrans-dev_1.4.0-1_all.deb": "9ce1af9464faee0c679348dd11cdf63934c12e734a64e0903692b0cb5af38e06",
-}
diff --git a/debian/python_jinja2.BUILD b/debian/python_jinja2.BUILD
deleted file mode 100644
index 5e564c9..0000000
--- a/debian/python_jinja2.BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-py_library(
-    name = "python_jinja2",
-    srcs = glob(["src/jinja2/*.py"]),
-    imports = ["src/"],
-    target_compatible_with = [
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-    deps = ["@python_markupsafe"],
-)
diff --git a/debian/python_markupsafe.BUILD b/debian/python_markupsafe.BUILD
deleted file mode 100644
index af6cf56..0000000
--- a/debian/python_markupsafe.BUILD
+++ /dev/null
@@ -1,9 +0,0 @@
-py_library(
-    name = "python_markupsafe",
-    srcs = glob(["src/markupsafe/*.py"]),
-    imports = ["src/"],
-    target_compatible_with = [
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-)
diff --git a/debian/python_shapely_init.patch b/debian/python_shapely_init.patch
deleted file mode 100644
index 0304760..0000000
--- a/debian/python_shapely_init.patch
+++ /dev/null
@@ -1,8 +0,0 @@
---- a/__init__.py	2018-10-17 21:55:40.000000000 -0700
-+++ b/__init__.py	2018-10-17 22:54:48.798723540 -0700
-@@ -1 +1,5 @@
- __version__ = "1.4.3"
-+
-+import os
-+_base = os.path.dirname(os.path.dirname(__file__))
-+os.environ['LIBRARY_PATH'] = os.path.join(_base, 'rpathed', 'usr', 'lib')
diff --git a/debian/python_yapf.BUILD b/debian/python_yapf.BUILD
deleted file mode 100644
index ad06576..0000000
--- a/debian/python_yapf.BUILD
+++ /dev/null
@@ -1,9 +0,0 @@
-py_binary(
-    name = "python_yapf",
-    srcs = glob(["yapf/**/*.py"]),
-    main = "yapf/__main__.py",
-    target_compatible_with = [
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-)
diff --git a/debian/qdldl_python.BUILD b/debian/qdldl_python.BUILD
deleted file mode 100644
index d270029..0000000
--- a/debian/qdldl_python.BUILD
+++ /dev/null
@@ -1,17 +0,0 @@
-py_library(
-    name = "python_qdldl",
-    srcs = glob(["**/*.py"]),
-    data = glob(
-        include = ["**/*"],
-        exclude = ["**/*.py"],
-    ),
-    imports = ["."],
-    target_compatible_with = [
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-    deps = [
-        "@python_repo//:numpy",
-        "@python_repo//:scipy",
-    ],
-)
diff --git a/debian/six.BUILD b/debian/six.BUILD
deleted file mode 100644
index 921bffb..0000000
--- a/debian/six.BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-py_library(
-    name = "six",
-    srcs = [
-        "six.py",
-    ],
-    target_compatible_with = [
-        "@//tools/platforms/python:debian_bundled_python",
-    ],
-    visibility = ["//visibility:public"],
-)
diff --git a/frc971/analysis/BUILD b/frc971/analysis/BUILD
index 9904132..b50e84e 100644
--- a/frc971/analysis/BUILD
+++ b/frc971/analysis/BUILD
@@ -17,8 +17,8 @@
         "//aos/events:shm_event_loop",
         "//aos/events:simulated_event_loop",
         "//aos/events/logging:log_reader",
+        "//third_party/python",
         "@com_github_google_glog//:glog",
-        "@python_repo//:python3.9_lib",
     ],
 )
 
diff --git a/frc971/control_loops/python/BUILD b/frc971/control_loops/python/BUILD
index f3dcb48..7b46439 100644
--- a/frc971/control_loops/python/BUILD
+++ b/frc971/control_loops/python/BUILD
@@ -1,5 +1,23 @@
 package(default_visibility = ["//visibility:public"])
 
+py_library(
+    name = "constants",
+    srcs = ["constants.py"],
+    deps = [
+        "@pip//pygobject",
+    ],
+)
+
+py_library(
+    name = "drawing_constants",
+    srcs = ["drawing_constants.py"],
+    deps = [
+        ":color",
+        "@pip//numpy",
+        "@pip//pygobject",
+    ],
+)
+
 py_binary(
     name = "haptic_wheel",
     srcs = [
@@ -8,10 +26,10 @@
     legacy_create_init = False,
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -30,8 +48,8 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-glog",
-        "@python_repo//:scipy",
+        "@pip//glog",
+        "@pip//scipy",
     ],
 )
 
@@ -58,7 +76,7 @@
     deps = [
         ":controls",
         ":python_init",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//matplotlib",
     ],
 )
 
@@ -71,7 +89,7 @@
     deps = [
         ":controls",
         ":python_init",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//matplotlib",
     ],
 )
 
@@ -98,7 +116,7 @@
     deps = [
         ":libspline",
         ":python_init",
-        "@python_repo//:numpy",
+        "@pip//numpy",
     ],
 )
 
@@ -112,8 +130,8 @@
         ":controls",
         ":drivetrain",
         ":python_init",
-        "//external:python-glog",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
     ],
 )
 
@@ -134,7 +152,7 @@
         ":controls",
         "//aos/util:py_trapezoid_profile",
         "//frc971/control_loops:python_init",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//matplotlib",
     ],
 )
 
@@ -147,14 +165,13 @@
         ":controls",
         "//aos/util:py_trapezoid_profile",
         "//frc971/control_loops:python_init",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//matplotlib",
     ],
 )
 
 py_binary(
     name = "spline_graph",
     srcs = [
-        "color.py",
         "graph.py",
         "multispline.py",
         "path_edit.py",
@@ -171,10 +188,15 @@
     visibility = ["//visibility:public"],
     deps = [
         ":basic_window",
+        ":color",
+        ":constants",
+        ":drawing_constants",
         ":libspline",
         ":python_init",
-        "@matplotlib_repo//:matplotlib3",
-        "@python_gtk",
+        "@pip//matplotlib",
+        "@pip//numpy",
+        "@pip//pygobject",
+        "@pip//scipy",
     ],
 )
 
@@ -182,13 +204,13 @@
     name = "basic_window",
     srcs = [
         "basic_window.py",
-        "color.py",
     ],
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:public"],
     deps = [
+        ":constants",
         ":python_init",
-        "@python_gtk",
+        "@pip//pygobject",
     ],
 )
 
@@ -215,7 +237,7 @@
         ":controls",
         ":linear_system",
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
diff --git a/frc971/downloader/downloader.py b/frc971/downloader/downloader.py
index bed72b9..f45a162 100644
--- a/frc971/downloader/downloader.py
+++ b/frc971/downloader/downloader.py
@@ -103,7 +103,8 @@
         # permissions or the executables won't be visible to init.
         os.chmod(temp_dir, 0o775)
         # Starter needs to be SUID so we transition from lvuser to admin.
-        os.chmod(os.path.join(temp_dir, "starterd"), 0o775 | stat.S_ISUID)
+        if args.type != "pi":
+            os.chmod(os.path.join(temp_dir, "starterd"), 0o775 | stat.S_ISUID)
 
         rsync_cmd = ([
             "external/rsync/usr/bin/rsync",
diff --git a/frc971/rockpi/build_kernel.sh b/frc971/rockpi/build_kernel.sh
index f3cadd4..a230ecf 100755
--- a/frc971/rockpi/build_kernel.sh
+++ b/frc971/rockpi/build_kernel.sh
@@ -8,6 +8,10 @@
   ln -s ../.config linux/.config
 fi
 
+if [[ ! -e mali-driver ]]; then
+  git clone --branch master https://github.com/bootlin/mali-driver
+fi
+
 (
 cd linux
 
@@ -44,7 +48,13 @@
   make rockpi
 )
 
+(
+  cd mali-driver
+  make KDIR=$(realpath ../linux) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- all
+)
+
 cp ../../y2022/localizer/kernel/adis16505.ko "kernel-install/lib/modules/${VERSION}/kernel/"
+cp mali-driver/r8p0/drivers/gpu/arm/midgard/mali_kbase.ko "kernel-install/lib/modules/${VERSION}/kernel/"
 
 /sbin/depmod -b ./kernel-install ${VERSION}
 
diff --git a/frc971/rockpi/build_rootfs.sh b/frc971/rockpi/build_rootfs.sh
index e5706fc..f7abbd5 100755
--- a/frc971/rockpi/build_rootfs.sh
+++ b/frc971/rockpi/build_rootfs.sh
@@ -156,7 +156,10 @@
 target "apt-get -y install -t bullseye-backports systemd"
 target "apt-get -y install -t bullseye-backports bpfcc-tools"
 
-target "apt-get install -y sudo openssh-server python3 bash-completion git v4l-utils cpufrequtils pmount rsync vim-nox chrony libopencv-calib3d4.5 libopencv-contrib4.5 libopencv-core4.5 libopencv-features2d4.5 libopencv-flann4.5 libopencv-highgui4.5 libopencv-imgcodecs4.5 libopencv-imgproc4.5 libopencv-ml4.5 libopencv-objdetect4.5 libopencv-photo4.5 libopencv-shape4.5 libopencv-stitching4.5 libopencv-superres4.5 libopencv-video4.5 libopencv-videoio4.5 libopencv-videostab4.5 libopencv-viz4.5 libnice10 pmount libnice-dev feh libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 libgstreamer-plugins-bad1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-nice usbutils locales trace-cmd"
+target "apt-get install -y sudo openssh-server python3 bash-completion git v4l-utils cpufrequtils pmount rsync vim-nox chrony libopencv-calib3d4.5 libopencv-contrib4.5 libopencv-core4.5 libopencv-features2d4.5 libopencv-flann4.5 libopencv-highgui4.5 libopencv-imgcodecs4.5 libopencv-imgproc4.5 libopencv-ml4.5 libopencv-objdetect4.5 libopencv-photo4.5 libopencv-shape4.5 libopencv-stitching4.5 libopencv-superres4.5 libopencv-video4.5 libopencv-videoio4.5 libopencv-videostab4.5 libopencv-viz4.5 libnice10 pmount libnice-dev feh libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 libgstreamer-plugins-bad1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-nice usbutils locales trace-cmd clinfo"
+target "cd /tmp && wget https://software.frc971.org/Build-Dependencies/libmali-midgard-t86x-r14p0-x11_1.9-1_arm64.deb && sudo dpkg -i libmali-midgard-t86x-r14p0-x11_1.9-1_arm64.deb && rm libmali-midgard-t86x-r14p0-x11_1.9-1_arm64.deb"
+
+target "apt-get clean"
 
 target "usermod -a -G sudo pi"
 target "usermod -a -G video pi"
@@ -184,6 +187,7 @@
 copyfile root.root 644 etc/systemd/system/usb-mount@.service
 copyfile root.root 644 etc/udev/rules.d/99-usb-mount.rules
 copyfile root.root 644 etc/udev/rules.d/99-adis16505.rules
+copyfile root.root 644 etc/udev/rules.d/99-mali.rules
 
 target "apt-get update"
 target "apt-get -y install -t bullseye-backports systemd"
diff --git a/frc971/rockpi/contents/etc/udev/rules.d/99-adis16505.rules b/frc971/rockpi/contents/etc/udev/rules.d/99-adis16505.rules
index 02f591e..25145d2 100644
--- a/frc971/rockpi/contents/etc/udev/rules.d/99-adis16505.rules
+++ b/frc971/rockpi/contents/etc/udev/rules.d/99-adis16505.rules
@@ -1 +1 @@
-ACTION=="add",KERNEL=="adis16505",MODE="0666"
+SUBSYSTEM=="adis16505_class", MODE="0666", GROUP="dialout"
diff --git a/frc971/rockpi/contents/etc/udev/rules.d/99-mali.rules b/frc971/rockpi/contents/etc/udev/rules.d/99-mali.rules
new file mode 100644
index 0000000..2c8d63a
--- /dev/null
+++ b/frc971/rockpi/contents/etc/udev/rules.d/99-mali.rules
@@ -0,0 +1 @@
+KERNEL=="mali0", MODE="0660", GROUP="render"
diff --git a/frc971/vision/charuco_lib.cc b/frc971/vision/charuco_lib.cc
index 65ee4ad..c4d98d9 100644
--- a/frc971/vision/charuco_lib.cc
+++ b/frc971/vision/charuco_lib.cc
@@ -27,6 +27,11 @@
     "The mininum number of aruco targets in charuco board required to match.");
 DEFINE_bool(visualize, false, "Whether to visualize the resulting data.");
 
+DEFINE_uint32(age, 5, "Age to start dropping frames at.");
+DEFINE_uint32(disable_delay, 100, "Time after an issue to disable tracing at.");
+
+DECLARE_bool(enable_ftrace);
+
 namespace frc971 {
 namespace vision {
 namespace chrono = std::chrono;
@@ -100,7 +105,8 @@
           event_loop_->GetChannel<CameraImage>(channel)
               ->source_node()
               ->string_view())),
-      handle_image_(std::move(handle_image_fn)) {
+      handle_image_(std::move(handle_image_fn)),
+      timer_fn_(event_loop->AddTimer([this]() { DisableTracing(); })) {
   event_loop_->MakeWatcher(channel, [this](const CameraImage &image) {
     const monotonic_clock::time_point eof_source_node =
         monotonic_clock::time_point(
@@ -139,7 +145,20 @@
     const monotonic_clock::duration age = event_loop_->monotonic_now() - eof;
     const double age_double =
         std::chrono::duration_cast<std::chrono::duration<double>>(age).count();
-    if (age > std::chrono::milliseconds(100)) {
+    if (age > std::chrono::milliseconds(FLAGS_age)) {
+      if (FLAGS_enable_ftrace) {
+        ftrace_.FormatMessage("Too late receiving image, age: %f\n",
+                              age_double);
+        if (FLAGS_disable_delay > 0) {
+          if (!disabling_) {
+            timer_fn_->Setup(event_loop_->monotonic_now() +
+                             chrono::milliseconds(FLAGS_disable_delay));
+            disabling_ = true;
+          }
+        } else {
+          DisableTracing();
+        }
+      }
       VLOG(2) << "Age: " << age_double << ", getting behind, skipping";
       return;
     }
@@ -147,12 +166,30 @@
     cv::Mat image_color_mat(cv::Size(image.cols(), image.rows()), CV_8UC2,
                             (void *)image.data()->data());
     const cv::Size image_size(image.cols(), image.rows());
-    cv::Mat rgb_image(image_size, CV_8UC3);
-    cv::cvtColor(image_color_mat, rgb_image, cv::COLOR_YUV2BGR_YUYV);
-    handle_image_(rgb_image, eof);
+    switch (format_) {
+      case Format::GRAYSCALE: {
+        ftrace_.FormatMessage("Starting yuyv->greyscale\n");
+        cv::Mat gray_image(image_size, CV_8UC3);
+        cv::cvtColor(image_color_mat, gray_image, cv::COLOR_YUV2GRAY_YUYV);
+        handle_image_(gray_image, eof);
+      } break;
+      case Format::BGR: {
+        cv::Mat rgb_image(image_size, CV_8UC3);
+        cv::cvtColor(image_color_mat, rgb_image, cv::COLOR_YUV2BGR_YUYV);
+        handle_image_(rgb_image, eof);
+      } break;
+      case Format::YUYV2: {
+        handle_image_(image_color_mat, eof);
+      };
+    }
   });
 }
 
+void ImageCallback::DisableTracing() {
+  disabling_ = false;
+  ftrace_.TurnOffOrDie();
+}
+
 void CharucoExtractor::SetupTargetData() {
   // TODO(Jim): Put correct values here
   marker_length_ = 0.15;
diff --git a/frc971/vision/charuco_lib.h b/frc971/vision/charuco_lib.h
index 02bad18..c7269f9 100644
--- a/frc971/vision/charuco_lib.h
+++ b/frc971/vision/charuco_lib.h
@@ -49,15 +49,33 @@
 // full-service callback functionality
 class ImageCallback {
  public:
+  enum class Format {
+    YUYV2 = 0,
+    BGR = 1,
+    GRAYSCALE = 2,
+  };
   ImageCallback(aos::EventLoop *event_loop, std::string_view channel,
                 std::function<void(cv::Mat, aos::monotonic_clock::time_point)>
                     &&handle_image_fn);
 
+  void set_format(Format format) {
+    format_ = format;
+  }
+
  private:
+  void DisableTracing();
+
   aos::EventLoop *event_loop_;
   aos::Fetcher<aos::message_bridge::ServerStatistics> server_fetcher_;
   const aos::Node *source_node_;
   std::function<void(cv::Mat, aos::monotonic_clock::time_point)> handle_image_;
+  aos::TimerHandler *timer_fn_;
+
+  bool disabling_ = false;
+
+  aos::Ftrace ftrace_;
+
+  Format format_ = Format::BGR;
 };
 
 // Types of targets that a CharucoExtractor can detect in images
diff --git a/motors/fet12/BUILD b/motors/fet12/BUILD
index bf94f52..968c592 100644
--- a/motors/fet12/BUILD
+++ b/motors/fet12/BUILD
@@ -86,7 +86,7 @@
     ],
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
-        "@python_repo//:scipy",
+        "@pip//scipy",
     ],
 )
 
@@ -98,7 +98,7 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":calib_sensors",
-        "@python_repo//:scipy",
+        "@pip//scipy",
     ],
 )
 
diff --git a/motors/python/BUILD b/motors/python/BUILD
index 2b2b80b..0d9ea32 100644
--- a/motors/python/BUILD
+++ b/motors/python/BUILD
@@ -7,11 +7,11 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
-        "@python_repo//:scipy",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
+        "@pip//scipy",
     ],
 )
 
@@ -24,9 +24,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/motors/seems_reasonable/BUILD b/motors/seems_reasonable/BUILD
index a9eb95b..b899ffb 100644
--- a/motors/seems_reasonable/BUILD
+++ b/motors/seems_reasonable/BUILD
@@ -7,9 +7,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -23,9 +23,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/third_party/abseil/absl/BUILD.bazel b/third_party/abseil/absl/BUILD.bazel
index 6da20c4..769708e 100644
--- a/third_party/abseil/absl/BUILD.bazel
+++ b/third_party/abseil/absl/BUILD.bazel
@@ -28,22 +28,22 @@
 config_setting(
     name = "osx",
     constraint_values = [
-        "@bazel_tools//platforms:osx",
+        "@platforms//os:osx",
     ],
 )
 
 config_setting(
     name = "ios",
     constraint_values = [
-        "@bazel_tools//platforms:ios",
+        "@platforms//os:ios",
     ],
 )
 
 config_setting(
     name = "windows",
     constraint_values = [
-        "@bazel_tools//platforms:x86_64",
-        "@bazel_tools//platforms:windows",
+        "@platforms//cpu:x86_64",
+        "@platforms//os:windows",
     ],
     visibility = [":__subpackages__"],
 )
diff --git a/third_party/abseil/absl/time/internal/cctz/BUILD.bazel b/third_party/abseil/absl/time/internal/cctz/BUILD.bazel
index 45a9529..f549af6 100644
--- a/third_party/abseil/absl/time/internal/cctz/BUILD.bazel
+++ b/third_party/abseil/absl/time/internal/cctz/BUILD.bazel
@@ -26,14 +26,14 @@
 config_setting(
     name = "osx",
     constraint_values = [
-        "@bazel_tools//platforms:osx",
+        "@platforms//os:osx",
     ],
 )
 
 config_setting(
     name = "ios",
     constraint_values = [
-        "@bazel_tools//platforms:ios",
+        "@platforms//os:ios",
     ],
 )
 
diff --git a/third_party/matplotlib-cpp/BUILD b/third_party/matplotlib-cpp/BUILD
index bd10366..431a403 100644
--- a/third_party/matplotlib-cpp/BUILD
+++ b/third_party/matplotlib-cpp/BUILD
@@ -5,28 +5,18 @@
     hdrs = [
         "matplotlibcpp.h",
     ],
-    data = select({
-        "//tools/platforms/python:debian_bundled_python": [
-            "@matplotlib_repo//:matplotlib3",
-        ],
-        "//tools/platforms/python:upstream_bundled_python": [
-            "@pip//matplotlib",
-            "@pip//pygobject",
-        ],
-    }) + [
+    data = [
         "//third_party/python:python_runtime",
+        "@pip//matplotlib",
+        "@pip//pygobject",
     ],
     # While this is technically compatible with "linux", the
     # "@python_repo//:all_files" has x86 binaries in it.
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:public"],
-    deps = select({
-        "//tools/platforms/python:debian_bundled_python": [],
-        "//tools/platforms/python:upstream_bundled_python": [
-            "//third_party/python:numpy_cc",
-        ],
-    }) + [
+    deps = [
         "//third_party/python",
+        "//third_party/python:numpy_cc",
     ],
 )
 
diff --git a/third_party/python/BUILD b/third_party/python/BUILD
index 325870c..46c464c 100644
--- a/third_party/python/BUILD
+++ b/third_party/python/BUILD
@@ -2,37 +2,30 @@
 
 cc_library(
     name = "python",
-    deps = select({
-        "//tools/platforms/python:debian_bundled_python": [
-            "@python_repo//:python3.9_lib",
-        ],
-        "//tools/platforms/python:upstream_bundled_python": [
-            "@python3_9_x86_64-unknown-linux-gnu//:python_headers",
-            "@python3_9_x86_64-unknown-linux-gnu//:libpython",
-        ],
-    }),
-    defines = select({
-        "//tools/platforms/python:debian_bundled_python": [
-            "FRC971_DEBIAN_BUNDLED_PYTHON",
-        ],
-        "//tools/platforms/python:upstream_bundled_python": [
-            "FRC971_UPSTREAM_BUNDLED_PYTHON",
-            "FRC971_PYTHON_HOME=../python3_9_x86_64-unknown-linux-gnu/",
-        ],
-    }),
+    deps = [
+        "@python3_9_x86_64-unknown-linux-gnu//:python_headers",
+        "@python3_9_x86_64-unknown-linux-gnu//:libpython",
+    ],
+    defines = [
+        "FRC971_UPSTREAM_BUNDLED_PYTHON",
+        "FRC971_PYTHON_HOME=../python3_9_x86_64-unknown-linux-gnu/",
+    ],
+    target_compatible_with = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:linux",
+    ],
     visibility = ["//visibility:public"],
 )
 
 filegroup(
     name = "python_runtime",
-    data = select({
-        "//tools/platforms/python:debian_bundled_python": [
-            "@python_repo//:all_files",
-        ],
-        "//tools/platforms/python:upstream_bundled_python": [
-            "@python3_9_x86_64-unknown-linux-gnu//:files",
-        ],
-    }),
+    data = [
+        "@python3_9_x86_64-unknown-linux-gnu//:files",
+    ],
+    target_compatible_with = [
+        "@platforms//cpu:x86_64",
+        "@platforms//os:linux",
+    ],
     visibility = ["//visibility:public"],
 )
 
diff --git a/tools/bazel b/tools/bazel
index 72ca2f8..82a86fe 100755
--- a/tools/bazel
+++ b/tools/bazel
@@ -24,7 +24,7 @@
   exec "${BAZEL_OVERRIDE}" "$@"
 fi
 
-readonly VERSION="5.1.1"
+readonly VERSION="6.0.0"
 
 readonly DOWNLOAD_DIR="${HOME}/.cache/bazel"
 # Directory to unpack bazel into.  This must change whenever bazel changes.
diff --git a/tools/build_rules/BUILD b/tools/build_rules/BUILD
index aeb8517..111d9d0 100644
--- a/tools/build_rules/BUILD
+++ b/tools/build_rules/BUILD
@@ -10,7 +10,7 @@
     srcs = ["jinja2_generator.py"],
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//visibility:public"],
-    deps = ["@python_jinja2"],
+    deps = ["@pip//jinja2"],
 )
 
 py_binary(
@@ -22,5 +22,5 @@
     ],
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//visibility:public"],
-    deps = ["@python_jinja2"],
+    deps = ["@pip//jinja2"],
 )
diff --git a/tools/ci/buildkite.yaml b/tools/ci/buildkite.yaml
index 30a7e24..e20c523 100644
--- a/tools/ci/buildkite.yaml
+++ b/tools/ci/buildkite.yaml
@@ -19,13 +19,6 @@
       - tools/ci/clean-disk.sh
       - tools/bazel ${STARTUP} --output_base=../k8_output_base test ${COMMON} --config=k8 --config=eigen ${TARGETS}
 
-  # Temporary step until I manage to migrate everything over.
-  # TODO(phil): Delete this step.
-  - label: "x86_64_upstream_python"
-    commands:
-      - tools/ci/clean-disk.sh
-      - tools/bazel ${STARTUP} --output_base=../k8_output_base_upstream_python test ${COMMON} --config=k8_upstream_python --config=eigen --build_tests_only --test_lang_filters=py ${TARGETS}
-
   - label: "roborio"
     commands:
       - tools/ci/clean-disk.sh
diff --git a/tools/lint/BUILD b/tools/lint/BUILD
index fedead2..58ef879 100644
--- a/tools/lint/BUILD
+++ b/tools/lint/BUILD
@@ -1,4 +1,5 @@
 load("@ci_configure//:ci.bzl", "RUNNING_IN_CI")
+load("@pip_deps//:requirements.bzl", "entry_point")
 
 sh_binary(
     name = "gofmt",
@@ -38,7 +39,7 @@
     name = "yapf",
     srcs = ["yapf.sh"],
     data = [
-        "@python_yapf",
+        entry_point("yapf"),
     ],
     deps = [
         "@bazel_tools//tools/bash/runfiles",
diff --git a/tools/lint/run-ci.sh b/tools/lint/run-ci.sh
index a05aa0f..6e026ee 100755
--- a/tools/lint/run-ci.sh
+++ b/tools/lint/run-ci.sh
@@ -111,12 +111,11 @@
 # All the linters that we are going to run.
 readonly -a LINTERS=(
     gofmt
-    # TODO(phil): Re-enable these. No idea what's going on.
-    #gomod
-    #update_go_repos
-    #gazelle
-    #tweak_gazelle_go_deps
-    #clean_up_go_mirrors
+    gomod
+    update_go_repos
+    gazelle
+    tweak_gazelle_go_deps
+    clean_up_go_mirrors
     rustfmt
     cargo_lockfile
     cargo_raze
diff --git a/tools/lint/yapf.sh b/tools/lint/yapf.sh
index 404d5fd..d88aa42 100755
--- a/tools/lint/yapf.sh
+++ b/tools/lint/yapf.sh
@@ -11,7 +11,8 @@
   { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
 # --- end runfiles.bash initialization v2 ---
 
-readonly YAPF="$(rlocation python_yapf/python_yapf)"
+YAPF="$(rlocation pip_deps_yapf/rules_python_wheel_entry_point_yapf)"
+readonly YAPF
 
 # Run everything from the root of the tree.
 cd "${BUILD_WORKSPACE_DIRECTORY}"
diff --git a/tools/platforms/BUILD b/tools/platforms/BUILD
index 6165b00..1d79f06 100644
--- a/tools/platforms/BUILD
+++ b/tools/platforms/BUILD
@@ -11,18 +11,6 @@
     ],
 )
 
-# TODO(phil): Delete this platform after migrating everything to use upstream
-# pip rules.
-platform(
-    name = "linux_x86_upstream_python",
-    constraint_values = [
-        "//tools/platforms/python:upstream_bundled_python",
-    ],
-    parents = [
-        ":linux_x86",
-    ],
-)
-
 platform(
     name = "linux_armv7",
     constraint_values = [
diff --git a/tools/platforms/python/BUILD b/tools/platforms/python/BUILD
deleted file mode 100644
index da13bbd..0000000
--- a/tools/platforms/python/BUILD
+++ /dev/null
@@ -1,21 +0,0 @@
-# TODO(phil): Delete this file after migrating everything to use upstream pip
-# rules.
-
-package(default_visibility = ["//visibility:public"])
-
-constraint_setting(
-    name = "python_source",
-    default_constraint_value = ":debian_bundled_python",
-)
-
-# Python code taken from .deb packages.
-constraint_value(
-    name = "debian_bundled_python",
-    constraint_setting = ":python_source",
-)
-
-# Python code taken from pypi.org.
-constraint_value(
-    name = "upstream_bundled_python",
-    constraint_setting = ":python_source",
-)
diff --git a/tools/python/BUILD b/tools/python/BUILD
index 78203bc..8e410e5 100644
--- a/tools/python/BUILD
+++ b/tools/python/BUILD
@@ -1,11 +1,28 @@
 load("@rules_python//python:defs.bzl", "py_runtime_pair")
 load("@rules_python//python:pip.bzl", "compile_pip_requirements")
 
+# Invoke this via "bazel run //tools/python:requirements.update".
+compile_pip_requirements(
+    name = "requirements",
+    requirements_in = "requirements.txt",
+    requirements_txt = "requirements.lock.txt",
+    tags = [
+        # The test pings pypi.org to make sure that the lock file matches the
+        # requirements file.
+        "requires-network",
+        # The test causes some packages' setup.py to be executed which can
+        # execute arbitrary code. Sometimes they look for compilers etc. That's
+        # not good for our hermeticity assumptions. Disable the test lock file
+        # test for now.
+        "manual",
+    ],
+)
+
 py_runtime(
     name = "python3_runtime",
     files = [
         "runtime_binary.sh",
-        "@python_repo//:all_files",
+        "@python3_9_x86_64-unknown-linux-gnu//:files",
     ],
     interpreter = "runtime_binary.sh",
     python_version = "PY3",
@@ -26,60 +43,11 @@
     target_compatible_with = [
         "@platforms//cpu:x86_64",
         "@platforms//os:linux",
-        "//tools/platforms/python:debian_bundled_python",
     ],
     toolchain = ":py_runtime",
     toolchain_type = "@rules_python//python:toolchain_type",
 )
 
-# Invoke this via "bazel run //tools/python:requirements.update".
-compile_pip_requirements(
-    name = "requirements",
-    requirements_in = "requirements.txt",
-    requirements_txt = "requirements.lock.txt",
-    tags = [
-        # The test pings pypi.org to make sure that the lock file matches the
-        # requirements file.
-        "requires-network",
-        # The test causes some packages' setup.py to be executed which can
-        # execute arbitrary code. Sometimes they look for compilers etc. That's
-        # not good for our hermeticity assumptions. Disable the test lock file
-        # test for now.
-        "manual",
-    ],
-)
-
-py_runtime(
-    name = "upstream_python3_runtime",
-    files = [
-        "runtime_binary.sh",
-        "@python3_9_x86_64-unknown-linux-gnu//:files",
-    ],
-    interpreter = "runtime_binary.sh",
-    python_version = "PY3",
-)
-
-py_runtime_pair(
-    name = "upstream_py_runtime",
-    py2_runtime = None,
-    py3_runtime = ":upstream_python3_runtime",
-)
-
-toolchain(
-    name = "upstream_python_toolchain",
-    exec_compatible_with = [
-        "@platforms//cpu:x86_64",
-        "@platforms//os:linux",
-    ],
-    target_compatible_with = [
-        "@platforms//cpu:x86_64",
-        "@platforms//os:linux",
-        "//tools/platforms/python:upstream_bundled_python",
-    ],
-    toolchain = ":upstream_py_runtime",
-    toolchain_type = "@rules_python//python:toolchain_type",
-)
-
 py_binary(
     name = "mirror_pip_packages",
     srcs = ["mirror_pip_packages.py"],
diff --git a/tools/python/generate_pip_packages_in_docker.sh b/tools/python/generate_pip_packages_in_docker.sh
index 13d51b7..f651918 100755
--- a/tools/python/generate_pip_packages_in_docker.sh
+++ b/tools/python/generate_pip_packages_in_docker.sh
@@ -115,6 +115,7 @@
   echo "Repairing wheel ${wheel}"
   if ! auditwheel show "${wheel_path}"; then
     echo "Assuming ${wheel} is a non-platform wheel. Skipping."
+    cp "${wheel_path}" "${SCRIPT_DIR}"/wheelhouse/
     continue
   fi
   auditwheel repair \
diff --git a/tools/python/package_annotations.bzl b/tools/python/package_annotations.bzl
index b359d72..cff14e7 100644
--- a/tools/python/package_annotations.bzl
+++ b/tools/python/package_annotations.bzl
@@ -9,4 +9,7 @@
         data = ["@gtk_runtime//:gtk_runtime"],
         deps = ["@bazel_tools//tools/python/runfiles"],
     ),
+    "python-gflags": package_annotation(
+        deps = ["@pip_deps_six//:pkg"],
+    ),
 }
diff --git a/tools/python/pip_configure.py b/tools/python/pip_configure.py
index 4f9dff1..349b945 100644
--- a/tools/python/pip_configure.py
+++ b/tools/python/pip_configure.py
@@ -13,10 +13,21 @@
 in BUILD files.
 """
 
+import re
 import sys
 import textwrap
 from pathlib import Path
 
+# Regex to parse the lines in a requirements file.
+# - Ignore line comments.
+# - Remove any inline comments that may or may not exist.
+# - Also remove any version specifiers. We don't use it.
+#
+# E.g:
+#  numpy==1.2.3  # needed because we like it.
+# turns into "numpy".
+REQUIREMENT_MATCH = re.compile(r"[-_.a-zA-Z0-9]+")
+
 
 def parse_requirements(requirements_path: Path) -> list[str]:
     """Parses tools/python/requirements.txt.
@@ -25,18 +36,9 @@
     depend on explicitly requested pip packages. We don't want users to depend
     on transitive dependencies of our requested pip packages.
     """
-    result = []
-    for line in requirements_path.read_text().splitlines():
-        # Ignore line comments.
-        if not line or line.startswith("#"):
-            continue
-
-        # Remove any inline comments that may or may not exist.
-        # E.g:
-        # numpy==1.2.3  # needed because we like it.
-        result.append(line.split()[0])
-
-    return result
+    lines = requirements_path.read_text().splitlines()
+    matches = map(REQUIREMENT_MATCH.match, lines)
+    return [match.group(0) for match in matches if match]
 
 
 def generate_build_files(requirements: list[str]) -> None:
@@ -62,9 +64,6 @@
                 name = "{requirement}",
                 deps = [requirement("{requirement}")],
                 visibility = ["//visibility:public"],
-                target_compatible_with = [
-                    "@//tools/platforms/python:upstream_bundled_python",
-                ],
             )
             """))
 
diff --git a/tools/python/requirements.lock.txt b/tools/python/requirements.lock.txt
index 93ac065..7470cae 100644
--- a/tools/python/requirements.lock.txt
+++ b/tools/python/requirements.lock.txt
@@ -99,6 +99,10 @@
     --hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \
     --hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343
     # via mkdocs
+glog==0.3.1 \
+    --hash=sha256:88cee83dea8bddf73db7edbf5bd697237628389ef476c0a0ecad639c606189e5 \
+    --hash=sha256:b721edef6009eabc0b4d9f2619e153d2627a7b71a3657c8ed69f02ef7c78be97
+    # via -r tools/python/requirements.txt
 idna==3.4 \
     --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
     --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
@@ -319,6 +323,7 @@
     #   osqp
     #   qdldl
     #   scipy
+    #   shapely
 opencv-python==4.6.0.66 \
     --hash=sha256:0dc82a3d8630c099d2f3ac1b1aabee164e8188db54a786abb7a4e27eba309440 \
     --hash=sha256:5af8ba35a4fcb8913ffb86e92403e9a656a4bff4a645d196987468f0f8947875 \
@@ -456,6 +461,11 @@
     # via
     #   ghp-import
     #   matplotlib
+python-gflags==3.1.2 \
+    --hash=sha256:40ae131e899ef68e9e14aa53ca063839c34f6a168afe622217b5b875492a1ee2
+    # via
+    #   -r tools/python/requirements.txt
+    #   glog
 pyyaml==6.0 \
     --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \
     --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
@@ -552,10 +562,52 @@
     #   -r tools/python/requirements.txt
     #   osqp
     #   qdldl
+shapely==2.0.0 \
+    --hash=sha256:11f1b1231a6c04213fb1226c6968d1b1b3b369ec42d1e9655066af87631860ea \
+    --hash=sha256:13a9f978cd287e0fa95f39904a2bb36deddab490e4fab8bf43eba01b7d9eb58f \
+    --hash=sha256:17d0f89581aa15f7887052a6adf2753f9fe1c3fdbb6116653972e0d43e720e65 \
+    --hash=sha256:21ba32a6c45b7f8ab7d2d8d5cf339704e2d1dfdf3e2fb465b950a0c9bc894a4f \
+    --hash=sha256:2287d0cb592c1814e9f48065888af7ee3f13e090e6f7fa3e208b06a83fb2f6af \
+    --hash=sha256:292c22ff7806e3a25bc4324295e9204169c61a09165d4c9ee0a9784c1709c85e \
+    --hash=sha256:40c397d67ba609a163d38b649eee2b06c5f9bdc86d244a8e4cd09c6e2791cf3c \
+    --hash=sha256:44198fc188fe4b7dd39ef0fd325395d1d6ab0c29a7bbaa15663a16c362bf6f62 \
+    --hash=sha256:5477be8c11bf3109f7b804bb2d57536538b8d0a6118207f1020d71338f1a827c \
+    --hash=sha256:550f110940d79931b6a12a17de07f6b158c9586c4b121f885af11458ae5626d7 \
+    --hash=sha256:56c0e70749f8c2956493e9333375d2e2264ce25c838fc49c3a2ececbf2d3ba92 \
+    --hash=sha256:5fe8649aafe6adcb4d90f7f735f06ca8ca02a16da273d901f1dd02afc0d3618e \
+    --hash=sha256:6c71738702cf5c3fc60b3bbe869c321b053ea754f57addded540a71c78c2612e \
+    --hash=sha256:7266080d39946395ba4b31fa35b9b7695e0a4e38ccabf0c67e2936caf9f9b054 \
+    --hash=sha256:73771b3f65c2949cce0b310b9b62b8ce069407ceb497a9dd4436f9a4d059f12c \
+    --hash=sha256:73d605fcefd06ee997ba307ef363448d355f3c3e81b3f56ed332eaf6d506e1b5 \
+    --hash=sha256:7b2c41514ba985ea3772eee9b386d620784cccb7a459a270a072f3ef01fdd807 \
+    --hash=sha256:820bee508e4a0e564db22f8b55bb5e6e7f326d8d7c103639c42f5d3f378f4067 \
+    --hash=sha256:8a7ba97c97d85c1f07c57f9524c45128ef2bf8279061945d78052c78862b357f \
+    --hash=sha256:8b9f780c3b79b4a6501e0e8833b1877841b7b0e0a243e77b529fda8f1030afc2 \
+    --hash=sha256:91bbca0378eb82f0808f0e59150ac0952086f4caaab87ad8515a5e55e896c21e \
+    --hash=sha256:99420c89af78f371b96f0e2bad9afdebc6d0707d4275d157101483e4c4049fd6 \
+    --hash=sha256:a391cae931976fb6d8d15a4f4a92006358e93486454a812dde1d64184041a476 \
+    --hash=sha256:a9b6651812f2caa23e4d06bc06a2ed34450f82cb1c110c170a25b01bbb090895 \
+    --hash=sha256:b1def13ec2a74ebda2210d2fc1c53cecce5a079ec90f341101399427874507f1 \
+    --hash=sha256:b3d97f3ce6df47ca68c2d64b8c3cfa5c8ccc0fbc81ef8e15ff6004a6426e71b1 \
+    --hash=sha256:c47a61b1cd0c5b064c6d912bce7dba78c01f319f65ecccd6e61eecd21861a37a \
+    --hash=sha256:c4b99a3456e06dc55482569669ece969cdab311f2ad2a1d5622fc770f68cf3cd \
+    --hash=sha256:d28e19791c9be2ba1cb2fddefa86f73364bdf8334e88dbcd78a8e4494c0af66b \
+    --hash=sha256:d486cab823f0a978964ae97ca10564ea2b2ced93e84a2ef0b7b62cbacec9d3d2 \
+    --hash=sha256:de3722c68e49fbde8cb6859695bbb8fb9a4d48bbdf34fcf38b7994d2bd9772e2 \
+    --hash=sha256:e4ed31658fd0799eaa3569982aab1a5bc8fcf25ec196606bf137ee4fa984be88 \
+    --hash=sha256:e991ad155783cd0830b895ec8f310fde9e79a7b283776b889a751fb1e7c819fc \
+    --hash=sha256:eab24b60ae96b7375adceb1f120be818c59bd69db0f3540dc89527d8a371d253 \
+    --hash=sha256:eaea9ddee706654026a84aceb9a3156105917bab3de58fcf150343f847478202 \
+    --hash=sha256:ef98fec4a3aca6d33e3b9fdd680fe513cc7d1c6aedc65ada8a3965601d9d4bcf \
+    --hash=sha256:f69c418f2040c8593e33b1aba8f2acf890804b073b817535b5d291139d152af5 \
+    --hash=sha256:f96b24da0242791cd6042f6caf074e7a4537a66ca2d1b57d423feb98ba901295
+    # via -r tools/python/requirements.txt
 six==1.16.0 \
     --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
     --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
-    # via python-dateutil
+    # via
+    #   glog
+    #   python-dateutil
 urllib3==1.26.13 \
     --hash=sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc \
     --hash=sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8
diff --git a/tools/python/requirements.txt b/tools/python/requirements.txt
index a4cbd6a..3b3ab3a 100644
--- a/tools/python/requirements.txt
+++ b/tools/python/requirements.txt
@@ -11,4 +11,9 @@
 pygobject
 requests
 scipy
+shapely
 yapf
+
+# TODO(phil): Migrate to absl-py. These are abandoned as far as I can tell.
+python-gflags
+glog
diff --git a/tools/python/runtime_binary.sh b/tools/python/runtime_binary.sh
index 6dc34c1..d251a0b 100755
--- a/tools/python/runtime_binary.sh
+++ b/tools/python/runtime_binary.sh
@@ -29,12 +29,6 @@
     LD_LIBRARY_PATH+=":${path}/../gtk_runtime/usr/lib"
     export LD_LIBRARY_PATH
     break
-  elif [[ "$path" == *.runfiles/python_repo ]]; then
-    PYTHON_BIN="$path"/usr/bin/python3
-    LD_LIBRARY_PATH="${path}/lib/x86_64-linux-gnu:${path}/usr/lib:${path}/usr/lib/x86_64-linux-gnu:${path}/../matplotlib_repo/usr/lib"
-    LD_LIBRARY_PATH+=":${path}/usr/lib/lapack:${path}/usr/lib/libblas:${path}/../matplotlib_repo/rpathed3/usr/lib:${path}/usr/lib/x86_64-linux-gnu/lapack:${path}/usr/lib/x86_64-linux-gnu/blas"
-    export LD_LIBRARY_PATH
-    break
   fi
 done
 
diff --git a/tools/python/whl_overrides.json b/tools/python/whl_overrides.json
index 3d724b9..108a544 100644
--- a/tools/python/whl_overrides.json
+++ b/tools/python/whl_overrides.json
@@ -27,6 +27,10 @@
         "sha256": "8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619",
         "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/ghp_import-2.1.0-py3-none-any.whl"
     },
+    "glog==0.3.1": {
+        "sha256": "88cee83dea8bddf73db7edbf5bd697237628389ef476c0a0ecad639c606189e5",
+        "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/glog-0.3.1-py2.py3-none-any.whl"
+    },
     "idna==3.4": {
         "sha256": "90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2",
         "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/idna-3.4-py3-none-any.whl"
@@ -103,6 +107,10 @@
         "sha256": "961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9",
         "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/python_dateutil-2.8.2-py2.py3-none-any.whl"
     },
+    "python_gflags==3.1.2": {
+        "sha256": "e2bd55abd9bb6e3b32026fd6c26a81c3f49979f24162fe73dc48da4fc306e74b",
+        "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/python_gflags-3.1.2-py3-none-any.whl"
+    },
     "pyyaml==6.0": {
         "sha256": "40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
         "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl"
@@ -123,6 +131,10 @@
         "sha256": "c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0",
         "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
     },
+    "shapely==2.0.0": {
+        "sha256": "91bbca0378eb82f0808f0e59150ac0952086f4caaab87ad8515a5e55e896c21e",
+        "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/shapely-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
+    },
     "six==1.16.0": {
         "sha256": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254",
         "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/six-1.16.0-py2.py3-none-any.whl"
diff --git a/y2014/control_loops/python/BUILD b/y2014/control_loops/python/BUILD
index 20035bc..5704405 100644
--- a/y2014/control_loops/python/BUILD
+++ b/y2014/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -39,11 +39,11 @@
     ],
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -57,10 +57,10 @@
     deps = [
         ":polydrivetrain_lib",
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -73,10 +73,10 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -89,10 +89,10 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2014_bot3/control_loops/python/BUILD b/y2014_bot3/control_loops/python/BUILD
index b63ca0a..ab15cf3 100644
--- a/y2014_bot3/control_loops/python/BUILD
+++ b/y2014_bot3/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2016/control_loops/python/BUILD b/y2016/control_loops/python/BUILD
index 3300e62..ad3fbe9 100644
--- a/y2016/control_loops/python/BUILD
+++ b/y2016/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -41,11 +41,11 @@
     visibility = ["//visibility:public"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -58,10 +58,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -74,10 +74,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -91,10 +91,10 @@
     deps = [
         ":python_init",
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -108,10 +108,10 @@
     deps = [
         ":python_init",
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -123,9 +123,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -137,10 +137,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -154,10 +154,10 @@
         ":shoulder_lib",
         ":wrist_lib",
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -173,9 +173,9 @@
         ":shoulder_lib",
         ":wrist_lib",
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2017/control_loops/python/BUILD b/y2017/control_loops/python/BUILD
index a6277e7..209bd70 100644
--- a/y2017/control_loops/python/BUILD
+++ b/y2017/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -39,10 +39,10 @@
     ],
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -55,10 +55,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -71,9 +71,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -86,10 +86,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:linear_system",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -103,10 +103,10 @@
     deps = [
         ":python_init",
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -120,10 +120,10 @@
     deps = [
         ":python_init",
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -135,9 +135,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         "//aos/util:py_trapezoid_profile",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -148,9 +148,9 @@
     ],
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -165,10 +165,10 @@
         ":indexer_lib",
         ":python_init",
         ":turret_lib",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2018/control_loops/python/BUILD b/y2018/control_loops/python/BUILD
index 772c7e1..35f7433 100644
--- a/y2018/control_loops/python/BUILD
+++ b/y2018/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -41,11 +41,11 @@
     visibility = ["//visibility:public"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -59,10 +59,10 @@
     deps = [
         ":polydrivetrain_lib",
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -76,9 +76,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -91,10 +91,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//glog",
+        "@pip//matplotlib",
+        "@pip//python_gflags",
     ],
 )
 
@@ -122,7 +122,9 @@
         ":python_init",
         "//frc971/control_loops/python:basic_window",
         "//frc971/control_loops/python:color",
-        "@python_gtk",
+        "@pip//numpy",
+        "@pip//pygobject",
+        "@pip//shapely",
     ],
 )
 
@@ -136,7 +138,7 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":python_init",
-        "@python_repo//:numpy",
+        "@pip//numpy",
     ],
 )
 
@@ -155,8 +157,8 @@
     ],
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
diff --git a/y2019/control_loops/python/BUILD b/y2019/control_loops/python/BUILD
index 3d5e641..bab607c 100644
--- a/y2019/control_loops/python/BUILD
+++ b/y2019/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -41,11 +41,11 @@
     visibility = ["//visibility:public"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -66,10 +66,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:linear_system",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -82,10 +82,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -98,10 +98,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -114,9 +114,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:linear_system",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
diff --git a/y2020/control_loops/python/BUILD b/y2020/control_loops/python/BUILD
index 9886452..8a1f737 100644
--- a/y2020/control_loops/python/BUILD
+++ b/y2020/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -41,11 +41,11 @@
     visibility = ["//visibility:public"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -58,10 +58,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -74,10 +74,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -90,10 +90,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -106,10 +106,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -121,7 +121,7 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//matplotlib",
     ],
 )
 
@@ -135,8 +135,8 @@
     deps = [
         ":flywheel",
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -150,8 +150,8 @@
     deps = [
         ":flywheel",
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2020/vision/sift/BUILD b/y2020/vision/sift/BUILD
index 195a82e..1920cce 100644
--- a/y2020/vision/sift/BUILD
+++ b/y2020/vision/sift/BUILD
@@ -259,7 +259,7 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":sift_fbs_python",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
+        "@pip//opencv_python",
     ],
 )
 
diff --git a/y2020/vision/sift/fast_gaussian.bzl b/y2020/vision/sift/fast_gaussian.bzl
index 1560f6a..af87245 100644
--- a/y2020/vision/sift/fast_gaussian.bzl
+++ b/y2020/vision/sift/fast_gaussian.bzl
@@ -23,7 +23,7 @@
     objects = [f + ".o" for f in files] + [
         "fast_gaussian_runtime.o",
     ]
-    htmls = [f + ".html" for f in files]
+    htmls = [f + ".stmt.html" for f in files]
 
     native.genrule(
         name = "generate_fast_gaussian",
diff --git a/y2020/vision/sift/fast_gaussian_halide_generator.sh b/y2020/vision/sift/fast_gaussian_halide_generator.sh
index cec9995..e305f5b 100755
--- a/y2020/vision/sift/fast_gaussian_halide_generator.sh
+++ b/y2020/vision/sift/fast_gaussian_halide_generator.sh
@@ -58,6 +58,7 @@
   "${HALIDE}/lib/libHalide.a" \
   -lstdc++ -lpthread -ldl -lm -lz \
   "${SOURCE}" \
-  "${HALIDE}/tools/GenGen.cpp" \
+  "${HALIDE}/share/Halide/tools/GenGen.cpp" \
+  --std=gnu++17 \
   -ggdb3 \
   -o "${BINARY}"
diff --git a/y2020/vision/tools/python_code/BUILD b/y2020/vision/tools/python_code/BUILD
index d259ba7..4da3191 100644
--- a/y2020/vision/tools/python_code/BUILD
+++ b/y2020/vision/tools/python_code/BUILD
@@ -2,9 +2,9 @@
     name = "train_and_match",
     srcs = ["train_and_match.py"],
     deps = [
-        "//external:python-glog",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
-        "@python_repo//:scipy",
+        "@pip//glog",
+        "@pip//opencv_python",
+        "@pip//scipy",
     ],
 )
 
@@ -15,9 +15,9 @@
     ],
     deps = [
         ":train_and_match",
-        "//external:python-glog",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
-        "@python_repo//:scipy",
+        "@pip//glog",
+        "@pip//opencv_python",
+        "@pip//scipy",
     ],
 )
 
@@ -28,7 +28,7 @@
     ],
     deps = [
         ":define_training_data",
-        "//external:python-glog",
+        "@pip//glog",
     ],
 )
 
@@ -42,8 +42,8 @@
         ":camera_definition",
         ":define_training_data",
         ":train_and_match",
-        "//external:python-glog",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
+        "@pip//glog",
+        "@pip//opencv_python",
     ],
 )
 
@@ -76,10 +76,10 @@
     deps = [
         ":camera_definition",
         ":target_definition",
-        "//external:python-glog",
         "//y2020/vision/sift:sift_fbs_python",
         "@bazel_tools//tools/python/runfiles",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
+        "@pip//glog",
+        "@pip//opencv_python",
     ],
 )
 
@@ -95,10 +95,10 @@
     deps = [
         ":camera_definition",
         ":target_definition",
-        "//external:python-glog",
         "//y2020/vision/sift:sift_fbs_python",
         "@bazel_tools//tools/python/runfiles",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
+        "@pip//glog",
+        "@pip//opencv_python",
     ],
 )
 
@@ -149,10 +149,10 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":load_sift_training",
-        "//external:python-glog",
         "//y2020/vision/sift:sift_fbs_python",
         "@bazel_tools//tools/python/runfiles",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
+        "@pip//glog",
+        "@pip//opencv_python",
     ],
 )
 
diff --git a/y2021_bot3/control_loops/python/BUILD b/y2021_bot3/control_loops/python/BUILD
index 15dacaa..c4010ba 100644
--- a/y2021_bot3/control_loops/python/BUILD
+++ b/y2021_bot3/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -40,11 +40,11 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:public"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2022/constants.cc b/y2022/constants.cc
index 444e87d..e9aaa59 100644
--- a/y2022/constants.cc
+++ b/y2022/constants.cc
@@ -218,20 +218,17 @@
                                       0.0172237531191 - 0.0172237531191 +
                                       0.00443383743660001 - 0.0117667224279;
 
-      intake_front->potentiometer_offset =
-          2.79628370453323 - 0.0250288114832881 + 0.577152542437606 +
-          0.476513825677792 - 0.47869991531664 + 0.50529913945481 -
-          0.796768714398522 + 0.163696825540674 - 0.0963353449092312;
+      intake_front->potentiometer_offset = + 3.572389;
       intake_front->subsystem_params.zeroing_constants
-          .measured_absolute_position = 0.175014091275898;
+          .measured_absolute_position = 0.238611243887673;
 
       intake_back->potentiometer_offset =
           3.1409576474047 + 0.278653334013286 + 0.00879137908308503 +
           0.0837134053818833 + 0.832945730100298 - 0.00759895654985426 -
           2.03114758819475 + 0.318379597392509 + 0.675664531140745 +
-          0.0650864893911517;
+          0.0650864893911517 - 0.0202318432257168 - 0.0561212375592096;
       intake_back->subsystem_params.zeroing_constants
-          .measured_absolute_position = 0.0517274215962501;
+          .measured_absolute_position = 0.292329083185933;
 
       turret->potentiometer_offset =
           -9.99970387166721 + 0.06415943 + 0.073290115367682 -
diff --git a/y2022/control_loops/python/BUILD b/y2022/control_loops/python/BUILD
index 17f7b2a..ef274b8 100644
--- a/y2022/control_loops/python/BUILD
+++ b/y2022/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -40,11 +40,11 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:public"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -58,7 +58,7 @@
         "//aos/util:py_trapezoid_profile",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
-        "@matplotlib_repo//:matplotlib3",
+        "@pip//matplotlib",
     ],
 )
 
@@ -72,9 +72,9 @@
     deps = [
         ":catapult_lib",
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
-        "@osqp_amd64//:python_osqp",
+        "@pip//glog",
+        "@pip//osqp",
+        "@pip//python_gflags",
     ],
 )
 
@@ -87,10 +87,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -103,10 +103,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -119,10 +119,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:linear_system",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2022/vision/BUILD b/y2022/vision/BUILD
index 58dbba8..777d139 100644
--- a/y2022/vision/BUILD
+++ b/y2022/vision/BUILD
@@ -54,7 +54,7 @@
         "camera_definition.py",
     ],
     deps = [
-        "//external:python-glog",
+        "@pip//glog",
     ],
 )
 
@@ -70,10 +70,10 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":camera_definition",
-        "//external:python-glog",
         "//y2022/vision:calibration_fbs_python",
         "@bazel_tools//tools/python/runfiles",
-        "@opencv_contrib_nonfree_amd64//:python_opencv",
+        "@pip//glog",
+        "@pip//opencv_python",
     ],
 )
 
diff --git a/y2022_bot3/control_loops/python/BUILD b/y2022_bot3/control_loops/python/BUILD
index b2ce1be..65a6033 100644
--- a/y2022_bot3/control_loops/python/BUILD
+++ b/y2022_bot3/control_loops/python/BUILD
@@ -9,9 +9,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:drivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -25,9 +25,9 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -40,11 +40,11 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     visibility = ["//visibility:public"],
     deps = [
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:drivetrain",
         "//frc971/control_loops/python:polydrivetrain",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -57,10 +57,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:controls",
         "//frc971/control_loops/python:linear_system",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
@@ -73,10 +73,10 @@
     target_compatible_with = ["@platforms//cpu:x86_64"],
     deps = [
         ":python_init",
-        "//external:python-gflags",
-        "//external:python-glog",
         "//frc971/control_loops/python:angular_system",
         "//frc971/control_loops/python:controls",
+        "@pip//glog",
+        "@pip//python_gflags",
     ],
 )
 
diff --git a/y2023/BUILD b/y2023/BUILD
new file mode 100644
index 0000000..60764ce
--- /dev/null
+++ b/y2023/BUILD
@@ -0,0 +1,145 @@
+load("//frc971:downloader.bzl", "robot_downloader")
+load("//aos:config.bzl", "aos_config")
+load("//tools/build_rules:template.bzl", "jinja2_template")
+
+robot_downloader(
+    name = "pi_download",
+    binaries = [
+        "//y2023/vision:viewer",
+        "//y2022/localizer:imu_main",
+        "//y2022/localizer:localizer_main",
+        "//aos/events/logging:log_cat",
+    ],
+    data = [
+        ":aos_config",
+    ],
+    dirs = [
+        "//y2022/www:www_files",
+    ],
+    start_binaries = [
+        "//aos/events/logging:logger_main",
+        "//aos/network:message_bridge_client",
+        "//aos/network:message_bridge_server",
+        "//aos/network:web_proxy_main",
+        "//aos/starter:irq_affinity",
+        "//y2023/vision:camera_reader",
+    ],
+    target_compatible_with = ["//tools/platforms/hardware:raspberry_pi"],
+    target_type = "pi",
+)
+
+aos_config(
+    name = "aos_config",
+    src = "y2023.json",
+    flatbuffers = [
+        "//aos/network:message_bridge_client_fbs",
+        "//aos/network:message_bridge_server_fbs",
+        "//aos/network:timestamp_fbs",
+        "//frc971/input:robot_state_fbs",
+        "//frc971/vision:vision_fbs",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":config_imu",
+        ":config_logger",
+        ":config_pi1",
+        ":config_pi2",
+        ":config_pi3",
+        ":config_pi4",
+        ":config_roborio",
+    ],
+)
+
+[
+    aos_config(
+        name = "config_" + pi,
+        src = "y2023_" + pi + ".json",
+        flatbuffers = [
+            "//aos/network:message_bridge_client_fbs",
+            "//aos/network:message_bridge_server_fbs",
+            "//aos/network:timestamp_fbs",
+            "//aos/network:remote_message_fbs",
+            "//frc971/vision:vision_fbs",
+        ],
+        target_compatible_with = ["@platforms//os:linux"],
+        visibility = ["//visibility:public"],
+        deps = [
+            "//aos/events:aos_config",
+            "//frc971/control_loops/drivetrain:aos_config",
+            "//frc971/input:aos_config",
+        ],
+    )
+    for pi in [
+        "pi1",
+        "pi2",
+        "pi3",
+        "pi4",
+    ]
+]
+
+aos_config(
+    name = "config_imu",
+    src = "y2023_imu.json",
+    flatbuffers = [
+        "//aos/network:message_bridge_client_fbs",
+        "//aos/network:message_bridge_server_fbs",
+        "//aos/network:timestamp_fbs",
+        "//aos/network:remote_message_fbs",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//aos/events:aos_config",
+        "//frc971/control_loops/drivetrain:aos_config",
+    ],
+)
+
+aos_config(
+    name = "config_logger",
+    src = "y2023_logger.json",
+    flatbuffers = [
+        "//aos/network:message_bridge_client_fbs",
+        "//aos/network:message_bridge_server_fbs",
+        "//aos/network:timestamp_fbs",
+        "//aos/network:remote_message_fbs",
+        "//frc971/vision:vision_fbs",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//aos/events:aos_config",
+        "//frc971/control_loops/drivetrain:aos_config",
+        "//frc971/input:aos_config",
+    ],
+)
+
+aos_config(
+    name = "config_roborio",
+    src = "y2023_roborio.json",
+    flatbuffers = [
+        "//aos/network:remote_message_fbs",
+        "//aos/network:message_bridge_client_fbs",
+        "//aos/network:message_bridge_server_fbs",
+        "//aos/network:timestamp_fbs",
+        "//y2019/control_loops/drivetrain:target_selector_fbs",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        "//aos/events:aos_config",
+        "//frc971/autonomous:aos_config",
+        "//frc971/control_loops/drivetrain:aos_config",
+        "//frc971/input:aos_config",
+        "//frc971/wpilib:aos_config",
+    ],
+)
+
+[
+    jinja2_template(
+        name = "y2023_pi" + str(num) + ".json",
+        src = "y2023_pi_template.json",
+        parameters = {"NUM": str(num)},
+        target_compatible_with = ["@platforms//os:linux"],
+    )
+    for num in range(1, 6)
+]
diff --git a/y2023/vision/camera_reader.cc b/y2023/vision/camera_reader.cc
index 0e86c32..3466c83 100644
--- a/y2023/vision/camera_reader.cc
+++ b/y2023/vision/camera_reader.cc
@@ -2,6 +2,7 @@
 #include "absl/strings/str_split.h"
 #include "aos/events/shm_event_loop.h"
 #include "aos/init.h"
+#include "aos/realtime.h"
 #include "frc971/vision/media_device.h"
 #include "frc971/vision/v4l2_reader.h"
 
@@ -76,6 +77,7 @@
   aos::ShmEventLoop event_loop(&config.message());
 
   event_loop.SetRuntimeRealtimePriority(55);
+  event_loop.SetRuntimeAffinity(aos::MakeCpusetFromCpus({2}));
 
   RockchipV4L2Reader v4l2_reader(&event_loop, event_loop.epoll(),
                                  rkisp1_selfpath->device());
diff --git a/y2023/y2023.json b/y2023/y2023.json
new file mode 100644
index 0000000..76f0e52
--- /dev/null
+++ b/y2023/y2023.json
@@ -0,0 +1,23 @@
+{
+  "channel_storage_duration": 2000000000,
+  "maps": [
+    {
+      "match": {
+        "name": "/aos",
+        "type": "aos.RobotState"
+      },
+      "rename": {
+        "name": "/roborio/aos"
+      }
+    }
+  ],
+  "imports": [
+    "y2023_roborio.json",
+    "y2023_pi1.json",
+    "y2023_pi2.json",
+    "y2023_pi3.json",
+    "y2023_pi4.json",
+    "y2023_imu.json",
+    "y2023_logger.json"
+  ]
+}
diff --git a/y2023/y2023_imu.json b/y2023/y2023_imu.json
new file mode 100644
index 0000000..6b2ca7a
--- /dev/null
+++ b/y2023/y2023_imu.json
@@ -0,0 +1,380 @@
+{
+  "channels": [
+    {
+      "name": "/imu/aos",
+      "type": "aos.timing.Report",
+      "source_node": "imu",
+      "frequency": 50,
+      "num_senders": 20,
+      "max_size": 4096
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.logging.LogMessageFbs",
+      "source_node": "imu",
+      "frequency": 200,
+      "num_senders": 20
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.starter.Status",
+      "source_node": "imu",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "frequency": 50,
+      "num_senders": 20,
+      "logger_nodes": [
+        "roborio",
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 5,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ]
+        },
+        {
+          "name": "logger",
+          "priority": 5,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ]
+        }
+      ]
+    },
+    {
+      "name": "/imu/aos/remote_timestamps/roborio/imu/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 100,
+      "source_node": "imu",
+      "max_size": 208
+    },
+    {
+      "name": "/imu/aos/remote_timestamps/logger/imu/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 100,
+      "source_node": "imu",
+      "max_size": 208
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "imu",
+      "frequency": 10,
+      "num_senders": 2,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "roborio",
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 5,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ]
+        },
+        {
+          "name": "logger",
+          "priority": 5,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ]
+        }
+      ]
+    },
+    {
+      "name": "/imu/aos/remote_timestamps/roborio/imu/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "imu",
+      "max_size": 208
+    },
+    {
+      "name": "/imu/aos/remote_timestamps/logger/imu/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "imu",
+      "max_size": 208
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.message_bridge.ServerStatistics",
+      "source_node": "imu",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.message_bridge.ClientStatistics",
+      "source_node": "imu",
+      "frequency": 20,
+      "num_senders": 2
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.logging.DynamicLogCommand",
+      "source_node": "imu",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "imu",
+      "frequency": 15,
+      "num_senders": 2,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "roborio",
+        "logger"
+      ],
+      "max_size": 400,
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ],
+          "time_to_live": 5000000
+        },
+        {
+          "name": "logger",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/imu/aos/remote_timestamps/roborio/imu/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "imu",
+      "max_size": 208
+    },
+    {
+      "name": "/imu/aos/remote_timestamps/logger/imu/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "imu",
+      "max_size": 208
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "logger",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "imu",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/imu/logger/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.starter.Status",
+      "source_node": "logger",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "imu",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/imu/logger/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "roborio",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "imu",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/imu/roborio/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.starter.Status",
+      "source_node": "roborio",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "imu",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/imu/roborio/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/localizer",
+      "type": "frc971.IMUValuesBatch",
+      "source_node": "imu",
+      "frequency": 2200,
+      "max_size": 1600,
+      "num_senders": 2
+    }
+  ],
+  "applications": [
+    {
+      "name": "message_bridge_client",
+      "executable_name": "message_bridge_client",
+      "nodes": [
+        "imu"
+      ]
+    },
+    {
+      "name": "localizer",
+      "executable_name": "localizer_main",
+      /* TODO(james): Remove this once confident in the accelerometer code. */
+      "args": ["--ignore_accelerometer"],
+      "nodes": [
+        "imu"
+      ]
+    },
+    {
+      "name": "imu",
+      "executable_name": "imu_main",
+      "nodes": [
+        "imu"
+      ]
+    },
+    {
+      "name": "message_bridge_server",
+      "executable_name": "message_bridge_server",
+      "nodes": [
+        "imu"
+      ]
+    },
+    {
+      "name": "localizer_logger",
+      "executable_name": "logger_main",
+      "args": ["--logging_folder", "", "--snappy_compress"],
+      "nodes": [
+        "imu"
+      ]
+    },
+    {
+      "name": "web_proxy",
+      "executable_name": "web_proxy_main",
+      "nodes": [
+        "imu"
+      ]
+    }
+  ],
+  "maps": [
+    {
+      "match": {
+        "name": "/aos*",
+        "source_node": "imu"
+      },
+      "rename": {
+        "name": "/imu/aos"
+      }
+    }
+  ],
+  "nodes": [
+    {
+      "name": "imu",
+      "hostname": "imu",
+      "hostnames": [
+        "pi-971-5",
+        "pi-7971-5",
+        "pi-8971-5",
+        "pi-9971-5"
+      ],
+      "port": 9971
+    },
+    {
+      "name": "logger"
+    },
+    {
+      "name": "roborio"
+    }
+  ]
+}
diff --git a/y2023/y2023_logger.json b/y2023/y2023_logger.json
new file mode 100644
index 0000000..6ddc19f
--- /dev/null
+++ b/y2023/y2023_logger.json
@@ -0,0 +1,425 @@
+{
+  "channels": [
+    {
+      "name": "/roborio/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "roborio",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger" : "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes" : ["roborio"]
+        }
+      ]
+    },
+    {
+      "name": "/pi1/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "pi1",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 1,
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/pi2/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "pi2",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 1,
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/pi3/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "pi3",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 1,
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/pi4/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "pi4",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 1,
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.timing.Report",
+      "source_node": "logger",
+      "frequency": 50,
+      "num_senders": 20,
+      "max_size": 4096
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.logging.LogMessageFbs",
+      "source_node": "logger",
+      "frequency": 400,
+      "num_senders": 20
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.message_bridge.ServerStatistics",
+      "source_node": "logger",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.message_bridge.ClientStatistics",
+      "source_node": "logger",
+      "frequency": 20,
+      "max_size": 2000,
+      "num_senders": 2
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.logging.DynamicLogCommand",
+      "source_node": "logger",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.starter.Status",
+      "source_node": "logger",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "frequency": 50,
+      "num_senders": 20,
+      "max_size": 2000,
+      "logger_nodes": [
+        "roborio"
+      ],
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/roborio/logger/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "logger",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "roborio"
+      ],
+      "frequency": 10,
+      "num_senders": 2,
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/roborio/logger/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "logger",
+      "frequency": 15,
+      "num_senders": 2,
+      "max_size": 400,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "roborio",
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "pi1",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ]
+        },
+        {
+          "name": "pi2",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ]
+        },
+        {
+          "name": "pi3",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ]
+        },
+        {
+          "name": "pi4",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ]
+        },
+        {
+          "name": "imu",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ]
+        },
+        {
+          "name": "roborio",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ]
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/roborio/logger/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/imu/logger/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/pi1/logger/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/pi2/logger/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/pi3/logger/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/pi4/logger/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/camera",
+      "type": "frc971.vision.CameraImage",
+      "logger": "NOT_LOGGED",
+      "source_node": "logger",
+      "frequency": 100,
+      "max_size": 2600000,
+      "num_readers": 4,
+      "read_method": "PIN",
+      "num_senders": 1
+    },
+    {
+      "name": "/localizer",
+      "type": "frc971.IMUValuesBatch",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "source_node": "imu",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 3,
+          "time_to_live": 500000000
+        }
+      ]
+    }
+  ],
+  "maps": [
+    {
+      "match": {
+        "name": "/aos*",
+        "source_node": "logger"
+      },
+      "rename": {
+        "name": "/logger/aos"
+      }
+    },
+    {
+      "match": {
+        "name": "/camera*",
+        "source_node": "logger"
+      },
+      "rename": {
+        "name": "/logger/camera"
+      }
+    }
+
+  ],
+  "applications": [
+    {
+      "name": "logger_message_bridge_client",
+      "executable_name": "message_bridge_client",
+      "autostart": false,
+      "args": ["--rmem=8388608", "--rt_priority=16"],
+      "nodes": [
+        "logger"
+      ]
+    },
+    {
+      "name": "logger_message_bridge_server",
+      "executable_name": "message_bridge_server",
+      "autostart": false,
+      "args": ["--rt_priority=16"],
+      "nodes": [
+        "logger"
+      ]
+    },
+    {
+      "name": "image_logger",
+      "executable_name": "logger_main",
+      "autostart": false,
+      "args": ["--snappy_compress", "--logging_folder", "", "--snappy_compress", "--rotate_every", "60.0"],
+      "nodes": [
+        "logger"
+      ]
+    },
+    {
+      "name": "image_streamer",
+      "executable_name": "image_streamer_start.sh",
+      "autostart": false,
+      "nodes": [
+        "logger"
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "name": "logger",
+      "hostname": "pi6",
+      "hostnames": [
+        "pi-971-6",
+        "pi-9971-6",
+        "ASchuh-T480s",
+        "aschuh-3950x"
+      ],
+      "port": 9971
+    },
+    {
+      "name": "pi1"
+    },
+    {
+      "name": "pi2"
+    },
+    {
+      "name": "pi3"
+    },
+    {
+      "name": "roborio"
+    },
+    {
+      "name": "imu"
+    },
+    {
+      "name": "pi4"
+    }
+  ]
+}
diff --git a/y2023/y2023_pi_template.json b/y2023/y2023_pi_template.json
new file mode 100644
index 0000000..2bf45bb
--- /dev/null
+++ b/y2023/y2023_pi_template.json
@@ -0,0 +1,360 @@
+{
+  "channels": [
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.timing.Report",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 50,
+      "num_senders": 20,
+      "max_size": 4096
+    },
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.logging.LogMessageFbs",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 200,
+      "num_senders": 20
+    },
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.starter.Status",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 50,
+      "num_senders": 20,
+      "max_size": 2000,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "roborio",
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 5,
+          "time_to_live": 5000000
+        },
+        {
+          "name": "logger",
+          "priority": 5,
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 10,
+      "num_senders": 2,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "roborio",
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 5,
+          "time_to_live": 5000000
+        },
+        {
+          "name": "logger",
+          "priority": 5,
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.message_bridge.ServerStatistics",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.message_bridge.ClientStatistics",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 20,
+      "num_senders": 2
+    },
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.logging.DynamicLogCommand",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/pi{{ NUM }}/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 15,
+      "num_senders": 2,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "roborio",
+        "imu"
+      ],
+      "max_size": 200,
+      "destination_nodes": [
+        {
+          "name": "roborio",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ]
+        },
+        {
+          "name": "imu",
+          "priority": 1,
+          "time_to_live": 5000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ]
+        }
+      ]
+    },
+    {
+      "name": "/pi{{ NUM }}/aos/remote_timestamps/roborio/pi{{ NUM }}/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "pi{{ NUM }}",
+      "max_size": 208
+    },
+    {
+      "name": "/pi{{ NUM }}/aos/remote_timestamps/imu/pi{{ NUM }}/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "pi{{ NUM }}",
+      "max_size": 208
+    },
+    {
+      "name": "/imu/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "imu",
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "pi{{ NUM }}"
+      ],
+      "destination_nodes": [
+        {
+          "name": "pi{{ NUM }}",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/imu/aos/remote_timestamps/pi{{ NUM }}/imu/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "imu",
+      "max_size": 208
+    },
+    {
+      "name": "/pi{{ NUM }}/camera",
+      "type": "frc971.vision.CameraImage",
+      "source_node": "pi{{ NUM }}",
+      "frequency": 40,
+      "max_size": 2600000,
+      "num_readers": 4,
+      "read_method": "PIN",
+      "num_senders": 18
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "logger",
+      "destination_nodes": [
+        {
+          "name": "pi{{ NUM }}",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/pi{{ NUM }}/logger/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/logger/aos",
+      "type": "aos.starter.Status",
+      "source_node": "logger",
+      "destination_nodes": [
+        {
+          "name": "pi{{ NUM }}",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "logger"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/logger/aos/remote_timestamps/pi{{ NUM }}/logger/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "logger",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "roborio",
+      "destination_nodes": [
+        {
+          "name": "pi{{ NUM }}",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/pi{{ NUM }}/roborio/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.starter.Status",
+      "source_node": "roborio",
+      "destination_nodes": [
+        {
+          "name": "pi{{ NUM }}",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/pi{{ NUM }}/roborio/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    }
+  ],
+  "applications": [
+    {
+      "name": "message_bridge_client",
+      "executable_name": "message_bridge_client",
+      "args": ["--rt_priority=16"],
+      "user": "pi",
+      "nodes": [
+        "pi{{ NUM }}"
+      ]
+    },
+    {
+      "name": "irq_affinity",
+      "executable_name": "irq_affinity",
+      "user": "root",
+      "args": ["--user=pi"],
+      "nodes": [
+          "pi{{ NUM }}"
+      ]
+    },
+    {
+      "name": "message_bridge_server",
+      "executable_name": "message_bridge_server",
+      "user": "pi",
+      "nodes": [
+        "pi{{ NUM }}"
+      ]
+    },
+    {
+      "name": "web_proxy",
+      "executable_name": "web_proxy_main",
+      "user": "pi",
+      "nodes": [
+        "pi{{ NUM }}"
+      ]
+    },
+    {
+      "name": "camera_reader",
+      "executable_name": "camera_reader",
+      "args": ["--enable_ftrace"],
+      "user": "pi",
+      "nodes": [
+        "pi{{ NUM }}"
+      ]
+    }
+  ],
+  "maps": [
+    {
+      "match": {
+        "name": "/aos*",
+        "source_node": "pi{{ NUM }}"
+      },
+      "rename": {
+        "name": "/pi{{ NUM }}/aos"
+      }
+    },
+    {
+      "match": {
+        "name": "/camera*",
+        "source_node": "pi{{ NUM }}"
+      },
+      "rename": {
+        "name": "/pi{{ NUM }}/camera"
+      }
+    }
+  ],
+  "nodes": [
+    {
+      "name": "pi{{ NUM }}",
+      "hostname": "pi{{ NUM }}",
+      "hostnames": [
+        "pi-971-{{ NUM }}",
+        "pi-7971-{{ NUM }}",
+        "pi-8971-{{ NUM }}",
+        "pi-9971-{{ NUM }}"
+      ],
+      "port": 9971
+    },
+    {
+      "name": "logger"
+    },
+    {
+      "name": "imu"
+    },
+    {
+      "name": "roborio"
+    }
+  ]
+}
diff --git a/y2023/y2023_roborio.json b/y2023/y2023_roborio.json
new file mode 100644
index 0000000..f10d901
--- /dev/null
+++ b/y2023/y2023_roborio.json
@@ -0,0 +1,522 @@
+{
+  "channels": [
+    {
+      "name": "/roborio/aos",
+      "type": "aos.JoystickState",
+      "source_node": "roborio",
+      "frequency": 100,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes" : [
+        "imu",
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "imu",
+          "priority": 5,
+          "time_to_live": 50000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ]
+        },
+        {
+          "name": "logger",
+          "priority": 5,
+          "time_to_live": 50000000,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ]
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/imu/roborio/aos/aos-JoystickState",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 200,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/logger/roborio/aos/aos-JoystickState",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 200,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.RobotState",
+      "source_node": "roborio",
+      "frequency": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.timing.Report",
+      "source_node": "roborio",
+      "frequency": 50,
+      "num_senders": 20,
+      "max_size": 4096
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.logging.LogMessageFbs",
+      "source_node": "roborio",
+      "frequency": 500,
+      "max_size": 344,
+      "num_senders": 20
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.starter.Status",
+      "source_node": "roborio",
+      "frequency": 50,
+      "num_senders": 20,
+      "max_size": 2000,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/logger/roborio/aos/aos-starter-Status",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.starter.StarterRpc",
+      "source_node": "roborio",
+      "frequency": 10,
+      "max_size": 400,
+      "num_senders": 2,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "logger"
+      ],
+      "destination_nodes": [
+        {
+          "name": "logger",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/logger/roborio/aos/aos-starter-StarterRpc",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 20,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.message_bridge.ServerStatistics",
+      "source_node": "roborio",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.message_bridge.ClientStatistics",
+      "source_node": "roborio",
+      "frequency": 20,
+      "max_size": 2000,
+      "num_senders": 2
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.logging.DynamicLogCommand",
+      "source_node": "roborio",
+      "frequency": 10,
+      "num_senders": 2
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/logger/roborio/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 200,
+      "source_node": "roborio"
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/pi1/roborio/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "roborio",
+      "max_size": 208
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/pi2/roborio/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "roborio",
+      "max_size": 208
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/pi3/roborio/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "roborio"
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/pi4/roborio/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "roborio"
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/imu/roborio/aos/aos-message_bridge-Timestamp",
+      "type": "aos.message_bridge.RemoteMessage",
+      "frequency": 20,
+      "source_node": "roborio",
+      "max_size": 208
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "aos.message_bridge.Timestamp",
+      "source_node": "roborio",
+      "frequency": 15,
+      "num_senders": 2,
+      "max_size": 512,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "pi1",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        },
+        {
+          "name": "pi2",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        },
+        {
+          "name": "pi3",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        },
+        {
+          "name": "pi4",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        },
+        {
+          "name": "imu",
+          "priority": 1,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "roborio"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.sensors.GyroReading",
+      "source_node": "roborio",
+      "frequency": 200,
+      "num_senders": 2
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.sensors.Uid",
+      "source_node": "roborio",
+      "frequency": 200,
+      "num_senders": 2
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.control_loops.drivetrain.fb.Trajectory",
+      "source_node": "roborio",
+      "max_size": 600000,
+      "frequency": 10,
+      "logger": "NOT_LOGGED",
+      "num_senders": 2,
+      "read_method": "PIN",
+      "num_readers": 10
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.control_loops.drivetrain.SplineGoal",
+      "source_node": "roborio",
+      "frequency": 10
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.control_loops.drivetrain.Goal",
+      "source_node": "roborio",
+      "max_size": 224,
+      "frequency": 200
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.control_loops.drivetrain.Position",
+      "source_node": "roborio",
+      "frequency": 400,
+      "max_size": 112,
+      "num_senders": 2
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.control_loops.drivetrain.Output",
+      "source_node": "roborio",
+      "frequency": 400,
+      "max_size": 80,
+      "num_senders": 2,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "imu",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ],
+          "time_to_live": 5000000
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/imu/drivetrain/frc971-control_loops-drivetrain-Output",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 400,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.control_loops.drivetrain.Status",
+      "source_node": "roborio",
+      "frequency": 400,
+      "max_size": 1616,
+      "num_senders": 2
+    },
+    {
+      "name": "/drivetrain",
+      "type": "frc971.control_loops.drivetrain.LocalizerControl",
+      "source_node": "roborio",
+      "frequency": 200,
+      "max_size": 96,
+      "logger": "LOCAL_AND_REMOTE_LOGGER",
+      "logger_nodes": [
+        "imu"
+      ],
+      "destination_nodes": [
+        {
+          "name": "imu",
+          "priority": 5,
+          "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+          "timestamp_logger_nodes": [
+            "imu"
+          ],
+          "time_to_live": 0
+        }
+      ]
+    },
+    {
+      "name": "/roborio/aos/remote_timestamps/imu/drivetrain/frc971-control_loops-drivetrain-LocalizerControl",
+      "type": "aos.message_bridge.RemoteMessage",
+      "source_node": "roborio",
+      "logger": "NOT_LOGGED",
+      "frequency": 400,
+      "num_senders": 2,
+      "max_size": 200
+    },
+    {
+      "name": "/drivetrain",
+      "type": "y2019.control_loops.drivetrain.TargetSelectorHint",
+      "source_node": "roborio"
+    },
+    {
+      "name": "/autonomous",
+      "type": "aos.common.actions.Status",
+      "source_node": "roborio"
+    },
+    {
+      "name": "/autonomous",
+      "type": "frc971.autonomous.Goal",
+      "source_node": "roborio"
+    },
+    {
+      "name": "/autonomous",
+      "type": "frc971.autonomous.AutonomousMode",
+      "source_node": "roborio",
+      "frequency": 200
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "frc971.PDPValues",
+      "source_node": "roborio",
+      "frequency": 55,
+      "max_size": 368
+    },
+    {
+      "name": "/roborio/aos",
+      "type": "frc971.wpilib.PneumaticsToLog",
+      "source_node": "roborio",
+      "frequency": 50
+    }
+  ],
+  "applications": [
+    {
+      "name": "drivetrain",
+      "executable_name": "drivetrain",
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "trajectory_generator",
+      "executable_name": "trajectory_generator",
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "joystick_reader",
+      "executable_name": "joystick_reader",
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "wpilib_interface",
+      "executable_name": "wpilib_interface",
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "autonomous_action",
+      "executable_name": "autonomous_action",
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "web_proxy",
+      "executable_name": "web_proxy_main",
+      "args": ["--min_ice_port=5800", "--max_ice_port=5810"],
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "roborio_message_bridge_client",
+      "executable_name": "message_bridge_client",
+      "args": ["--rt_priority=16"],
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "message_bridge_server",
+      "executable_name": "message_bridge_server",
+      "args": ["--rt_priority=16"],
+      "nodes": [
+        "roborio"
+      ]
+    },
+    {
+      "name": "logger",
+      "executable_name": "logger_main",
+      "args": ["--snappy_compress"],
+      "nodes": [
+        "roborio"
+      ]
+    }
+  ],
+  "maps": [
+    {
+      "match": {
+        "name": "/aos*",
+        "source_node": "roborio"
+      },
+      "rename": {
+        "name": "/roborio/aos"
+      }
+    }
+  ],
+  "nodes": [
+    {
+      "name": "roborio",
+      "hostname": "roborio",
+      "hostnames": [
+        "roboRIO-971-FRC",
+        "roboRIO-6971-FRC",
+        "roboRIO-7971-FRC",
+        "roboRIO-8971-FRC",
+        "roboRIO-9971-FRC"
+      ],
+      "port": 9971
+    },
+    {
+      "name": "imu"
+    },
+    {
+      "name": "logger"
+    },
+    {
+      "name": "pi1"
+    },
+    {
+      "name": "pi2"
+    },
+    {
+      "name": "pi3"
+    },
+    {
+      "name": "pi4"
+    }
+  ]
+}