Merge "app/radio: add support for following relays"
diff --git a/WORKSPACE b/WORKSPACE
index 47e7d02..a1b2b2c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,3 +1,7 @@
+workspace(
+    name = "hscloud",
+)
+
 load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
@@ -18,6 +22,7 @@
     name = "subpar",
     remote = "https://github.com/q3k/subpar",
     commit = "5dd9fb4586616c69df9b3f5aba12f08f85d708d1",
+    shallow_since = "1563277890 +0200",
 )
 
 # Docker rules
@@ -50,6 +55,7 @@
     name = "io_tweag_rules_nixpkgs",
     strip_prefix = "rules_nixpkgs-364adc70f32c2b3982995fce06b334f4d159070e",
     urls = ["https://github.com/tweag/rules_nixpkgs/archive/364adc70f32c2b3982995fce06b334f4d159070e.tar.gz"],
+    sha256 = "59cdaf64efe9391152564ccb670bd6976168fe1426ae8b9b2f676caef03a6849",
 )
 
 # Nix packages
@@ -71,7 +77,7 @@
 
 git_repository(
     name = "com_apt_itude_rules_pip",
-    commit = "ce667087818553cdc4b1a2258fc53df917c4f87c",
+    commit = "186bade4f054c0a9be7037585584c9c572cba483",
     remote = "https://github.com/apt-itude/rules_pip.git",
 )
 
@@ -83,11 +89,14 @@
 load("@com_apt_itude_rules_pip//rules:repository.bzl", "pip_repository")
 
 pip_repository(
-    name = "pip36",
-    python_interpreter = "python3.6",
-    requirements = "//pip:requirements-linux.txt",
+    name = "pydeps",
+    requirements = "//third_party/py:requirements-lock.json",
 )
 
+load("@pydeps//:requirements.bzl", "pip_install")
+
+pip_install()
+
 # stackb/rules_proto (for Python proto compilation)
 http_archive(
     name = "build_stack_rules_proto",
@@ -183,6 +192,7 @@
     name = "com_googlesource_gerrit_bazlets",
     remote = "https://gerrit.googlesource.com/bazlets",
     commit = "8528a0df69dadf6311d8d3f81c1b693afda8bcf1",
+    shallow_since = "1560842141 +0200",
 )
 
 load(
diff --git a/app/matrix/prod.jsonnet b/app/matrix/prod.jsonnet
index 447fb99..30edabe 100644
--- a/app/matrix/prod.jsonnet
+++ b/app/matrix/prod.jsonnet
@@ -18,7 +18,7 @@
 
         synapseImage: "informatic/synapse:v1.2.1-env-conf-rev2", // https://github.com/Informatic/synapse/tree/env_config (to be upstreamed...)
         riotImage: "bubuntux/riot-web:v1.3.2",
-        casProxyImage: "registry.k0.hswaw.net/informatic/oauth2-cas-proxy:0.1.4"
+        casProxyImage: "registry.k0.hswaw.net/q3k/oauth2-cas-proxy:0.1.4"
     },
 
     metadata(component):: {
diff --git a/bgpwtf/cccampix/BUILD b/bgpwtf/cccampix/BUILD
index db12375..a96f739 100644
--- a/bgpwtf/cccampix/BUILD
+++ b/bgpwtf/cccampix/BUILD
@@ -7,8 +7,8 @@
         "ripe-sync.py",
     ],
     deps = [
-        "@pip36//grpcio",
-        "@pip36//requests",
+        "@pydeps//grpcio",
+        "@pydeps//requests",
         "//bgpwtf/cccampix/proto:ix_py_proto",
     ],
     legacy_create_init = False,
diff --git a/bgpwtf/cccampix/frontend/BUILD.bazel b/bgpwtf/cccampix/frontend/BUILD.bazel
index 9b14017..6fae1d3 100644
--- a/bgpwtf/cccampix/frontend/BUILD.bazel
+++ b/bgpwtf/cccampix/frontend/BUILD.bazel
@@ -10,9 +10,9 @@
         "templates/**",
     ]),
     deps = [
-        "@pip36//arrow",
-        "@pip36//flask",
-        "@pip36//grpcio",
+        "@pydeps//arrow",
+        "@pydeps//flask",
+        "@pydeps//grpcio",
         "//bgpwtf/cccampix/proto:ix_py_proto",
     ],
 )
@@ -36,8 +36,8 @@
     ],
     deps = [
         ":frontend_lib",
-        "@pip36//gevent",
-        "@pip36//gunicorn",
+        "@pydeps//gevent",
+        "@pydeps//gunicorn",
     ],
     visibility = [
         "//bgpwtf/cccampix:__pkg__",
diff --git a/bgpwtf/cccampix/proto/BUILD.bazel b/bgpwtf/cccampix/proto/BUILD.bazel
index e8a54ba..22c557e 100644
--- a/bgpwtf/cccampix/proto/BUILD.bazel
+++ b/bgpwtf/cccampix/proto/BUILD.bazel
@@ -33,6 +33,6 @@
     srcs = ["ix_py_proto_src"],
     visibility = ["//visibility:public"],
     deps = [
-        "@pip36//protobuf",
+        "@pydeps//protobuf",
     ],
 )
diff --git a/cluster/README b/cluster/README
index efff049..b012b5c 100644
--- a/cluster/README
+++ b/cluster/README
@@ -35,7 +35,7 @@
 ------------------
 
  - bring up a new node with nixos, running the configuration.nix from bootstrap (to be documented)
- - `bazel run //cluster/clustercfg:clustercfg nodestrap bc01nXX.hswaw.net`
+ - `bazel run //cluster/clustercfg nodestrap bc01nXX.hswaw.net`
 
 Ceph - Debugging
 -----------------
diff --git a/cluster/clustercfg/BUILD b/cluster/clustercfg/BUILD
index 28a776d..eef2049 100644
--- a/cluster/clustercfg/BUILD
+++ b/cluster/clustercfg/BUILD
@@ -7,7 +7,7 @@
     ],
     visibility = ["//visibility:public"],
     deps = [
-        "@pip36//fabric",
+        "@pydeps//fabric",
         "//tools:secretstore_lib",
     ],
 )
diff --git a/cluster/kube/cluster.jsonnet b/cluster/kube/cluster.jsonnet
index 89ffdb0..09c3b33 100644
--- a/cluster/kube/cluster.jsonnet
+++ b/cluster/kube/cluster.jsonnet
@@ -135,6 +135,9 @@
         policies.AllowNamespaceInsecure("kube-system"),
         # TODO(q3k): fix this?
         policies.AllowNamespaceInsecure("ceph-waw2"),
+        policies.AllowNamespaceInsecure("matrix"),
+        policies.AllowNamespaceInsecure("registry"),
+        policies.AllowNamespaceInsecure("internet"),
     ],
 
     // Allow all service accounts (thus all controllers) to create secure pods.
diff --git a/cluster/kube/lib/registry.libsonnet b/cluster/kube/lib/registry.libsonnet
index a791acf..5272b2d 100644
--- a/cluster/kube/lib/registry.libsonnet
+++ b/cluster/kube/lib/registry.libsonnet
@@ -64,6 +64,7 @@
                 "config.yml": std.manifestYamlDoc({
                     version: "0.1",
                     log: {
+                        level: "debug",
                         fields: {
                             service: "registry",
                         },
@@ -248,7 +249,7 @@
                         },
                         containers_: {
                             registry: kube.Container("docker-registry") {
-                                image: "registry:2",
+                                image: "registry:2.7.1",
                                 args: ["/config/config.yml"],
                                 volumeMounts_: {
                                     config: { mountPath: "/config" },
diff --git a/personal/q3k/BUILD b/personal/q3k/BUILD
index 44988b4..99e661d 100644
--- a/personal/q3k/BUILD
+++ b/personal/q3k/BUILD
@@ -2,6 +2,6 @@
     name = "django-admin",
     srcs = ["django-admin.py"],
     deps = [
-        "@pip36//django",
+        "@pydeps36//django",
     ]
 )
diff --git a/personal/q3k/djtest/BUILD b/personal/q3k/djtest/BUILD
index 155b171..b3a2cad 100644
--- a/personal/q3k/djtest/BUILD
+++ b/personal/q3k/djtest/BUILD
@@ -2,7 +2,7 @@
     name = "app",
     srcs = glob(["djtest/**/*.py"]),
     deps = [
-        "@pip36//django",
+        "@pydeps//django",
     ],
 )
 
@@ -22,6 +22,6 @@
        "@bazel_tools//tools/python/runfiles",
     ],
     data = [
-        '@pip36//uwsgi/scripts:uwsgi',
+        '@pip__uWSGI_2_0_18_cp36_cp36m_linux_x86_64//scripts:uwsgi',
     ],
 )
diff --git a/personal/q3k/djtest/uwsgi-start.py b/personal/q3k/djtest/uwsgi-start.py
index 7597a6f..0edcdaf 100644
--- a/personal/q3k/djtest/uwsgi-start.py
+++ b/personal/q3k/djtest/uwsgi-start.py
@@ -9,8 +9,10 @@
 from bazel_tools.tools.python.runfiles import runfiles
 r = runfiles.Create()
 
-uwsgi = r.Rlocation("pip36/uwsgi/scripts/uwsgi")
-settings = r.Rlocation("__main__/personal/q3k/djtest/djtest/settings.py")
+uwsgi = r.Rlocation("pip__uWSGI_2_0_18_cp36_cp36m_linux_x86_64/scripts/uwsgi")
+print(uwsgi)
+settings = r.Rlocation("hscloud/personal/q3k/djtest/djtest/settings.py")
+print(settings)
 
 apppath = os.path.dirname(settings)
 sitepath = os.path.dirname(apppath)
diff --git a/pip/BUILD b/pip/BUILD
deleted file mode 100644
index c97dfc4..0000000
--- a/pip/BUILD
+++ /dev/null
@@ -1,12 +0,0 @@
-load("@com_apt_itude_rules_pip//rules:compile.bzl", "compile_pip_requirements")
-
-compile_pip_requirements(
-    name = "compile",
-    python_interpreter = "python3.6",
-    requirements_in = ":requirements.in",
-    requirements_txt = select({
-        "//:linux": "requirements-linux.txt",
-    }),
-)
-
-exports_files(glob(["requirements*"]))
diff --git a/pip/requirements-linux.txt b/pip/requirements-linux.txt
deleted file mode 100644
index e9b9ce3..0000000
--- a/pip/requirements-linux.txt
+++ /dev/null
@@ -1,298 +0,0 @@
-# This file is generated code. DO NOT EDIT.
-arrow==0.14.5 \
-    --hash=sha256:0186026cfd94ca4fb773f30cc5398289a3027480d335e0e5c0d2772643763137 \
-    --hash=sha256:a12de0124d812d15061ed36c7eb4a421fa1b95026a502a0b2062e9ea00fc4446
-asn1crypto==0.24.0 \
-    --hash=sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87 \
-    --hash=sha256:9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49
-bcrypt==3.1.5 \
-    --hash=sha256:05d8b762cb8a9bd0ad92ee95ed34b6119200a8760b625dadacfe88537ae691a3 \
-    --hash=sha256:136243dc44e5bab9b61206bd46fff3018bd80980b1a1dfbab64a22ff5745957f \
-    --hash=sha256:1de0df7a9ca76d68ec8122573ae584aab78dcfb728fc2c78ecafb15750b79465 \
-    --hash=sha256:214c720cfcd394ab9fd1cac59303847b0d45cc8feeb8126ec55619c77d85ec19 \
-    --hash=sha256:290e07820d408e8c81f79f848279b95cef693d6e6ce148fa6b1e573e89a4305b \
-    --hash=sha256:2d60412b11994ab91d25572f780f8461748cecdb6014c23e33b2ea0aabc99782 \
-    --hash=sha256:62ff976497590c7ef714f426aff8b908f2a11686364bb01cfc7d338e86a2ee27 \
-    --hash=sha256:77c99c50bd7ac4e9e9f948015c4638176ebe0a495b22b6ae4857f3ba077b12d8 \
-    --hash=sha256:9af0a7e135e7f5feca9c16ba33064af545b33b7297c1bb65daedb11c0fa653c4 \
-    --hash=sha256:9b08088fd103eedfd750d832819555d1f96bc8bec749c6d35a3f3de3b9e8c98d \
-    --hash=sha256:a185efb05ef8bac9a531474abfefb8323f3ec8d524d308d6720657eaeda068b5 \
-    --hash=sha256:c7a733c4c309c9ab572644cf7f8779845addcd5ecf474bb5c376f05731842c41 \
-    --hash=sha256:cc3f53fa3287c0fc2bc1636e9514b896d4777444b03d9e0e4f16762a856bfe8a \
-    --hash=sha256:d216ee4e8e64d43d819acaf8aa0db6cb518859072152cf35ada4987bf5c92bff \
-    --hash=sha256:db3c7d712c4049eff365f00c9236279602af17c0ba44ca759008641c7fd892b7 \
-    --hash=sha256:e1bb330c56ddec65ad9ce989e9e8664901ce96badfe47853a5ed03bfeb76f91a \
-    --hash=sha256:efcaace6e2915434d84e865c44f0cfe34e802269378afbb39a4aa6381aaec78b \
-    --hash=sha256:f4431e01f1a5fdea95c78758e24c9565651499d92024ff34663b1ab12c8a10e5 \
-    --hash=sha256:fd21155abee7cd4c0ba8fad5138636f2531174ea79ad1751b25dc30d833e1723
-certifi==2019.6.16 \
-    --hash=sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939 \
-    --hash=sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695
-cffi==1.11.5 \
-    --hash=sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743 \
-    --hash=sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef \
-    --hash=sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50 \
-    --hash=sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f \
-    --hash=sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30 \
-    --hash=sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93 \
-    --hash=sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257 \
-    --hash=sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b \
-    --hash=sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3 \
-    --hash=sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e \
-    --hash=sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc \
-    --hash=sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04 \
-    --hash=sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6 \
-    --hash=sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359 \
-    --hash=sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596 \
-    --hash=sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b \
-    --hash=sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd \
-    --hash=sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95 \
-    --hash=sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5 \
-    --hash=sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e \
-    --hash=sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6 \
-    --hash=sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca \
-    --hash=sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31 \
-    --hash=sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1 \
-    --hash=sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2 \
-    --hash=sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085 \
-    --hash=sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801 \
-    --hash=sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4 \
-    --hash=sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184 \
-    --hash=sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917 \
-    --hash=sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f \
-    --hash=sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb
-chardet==3.0.4 \
-    --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \
-    --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691
-click==7.0 \
-    --hash=sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13 \
-    --hash=sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7
-cryptography==2.4.2 \
-    --hash=sha256:05a6052c6a9f17ff78ba78f8e6eb1d777d25db3b763343a1ae89a7a8670386dd \
-    --hash=sha256:0eb83a24c650a36f68e31a6d0a70f7ad9c358fa2506dc7b683398b92e354a038 \
-    --hash=sha256:0ff4a3d6ea86aa0c9e06e92a9f986de7ee8231f36c4da1b31c61a7e692ef3378 \
-    --hash=sha256:1699f3e916981df32afdd014fb3164db28cdb61c757029f502cb0a8c29b2fdb3 \
-    --hash=sha256:1b1f136d74f411f587b07c076149c4436a169dc19532e587460d9ced24adcc13 \
-    --hash=sha256:21e63dd20f5e5455e8b34179ac43d95b3fb1ffa54d071fd2ed5d67da82cfe6dc \
-    --hash=sha256:2454ada8209bbde97065453a6ca488884bbb263e623d35ba183821317a58b46f \
-    --hash=sha256:3cdc5f7ca057b2214ce4569e01b0f368b3de9d8ee01887557755ccd1c15d9427 \
-    --hash=sha256:418e7a5ec02a7056d3a4f0c0e7ea81df374205f25f4720bb0e84189aa5fd2515 \
-    --hash=sha256:471a097076a7c4ab85561d7fa9a1239bd2ae1f9fd0047520f13d8b340bf3210b \
-    --hash=sha256:5ecaf9e7db3ca582c6de6229525d35db8a4e59dc3e8a40a331674ed90e658cbf \
-    --hash=sha256:63b064a074f8dc61be81449796e2c3f4e308b6eba04a241a5c9f2d05e882c681 \
-    --hash=sha256:6afe324dfe6074822ccd56d80420df750e19ac30a4e56c925746c735cf22ae8b \
-    --hash=sha256:70596e90398574b77929cd87e1ac6e43edd0e29ba01e1365fed9c26bde295aa5 \
-    --hash=sha256:70c2b04e905d3f72e2ba12c58a590817128dfca08949173faa19a42c824efa0b \
-    --hash=sha256:8908f1db90be48b060888e9c96a0dee9d842765ce9594ff6a23da61086116bb6 \
-    --hash=sha256:af12dfc9874ac27ebe57fc28c8df0e8afa11f2a1025566476b0d50cdb8884f70 \
-    --hash=sha256:b4fc04326b2d259ddd59ed8ea20405d2e695486ab4c5e1e49b025c484845206e \
-    --hash=sha256:da5b5dda4aa0d5e2b758cc8dfc67f8d4212e88ea9caad5f61ba132f948bab859
-django==2.2.3 \
-    --hash=sha256:4d23f61b26892bac785f07401bc38cbf8fa4cec993f400e9cd9ddf28fd51c0ea \
-    --hash=sha256:6e974d4b57e3b29e4882b244d40171d6a75202ab8d2402b8e8adbd182e25cf0c
-fabric==2.4.0 \
-    --hash=sha256:93684ceaac92e0b78faae551297e29c48370cede12ff0f853cdebf67d4b87068 \
-    --hash=sha256:98538f2f3f63cf52497a8d0b24d18424ae83fe67ac7611225c72afb9e67f2cf6
-flask==1.1.1 \
-    --hash=sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52 \
-    --hash=sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6
-future==0.17.1 \
-    --hash=sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8
-gevent==1.4.0 \
-    --hash=sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64 \
-    --hash=sha256:0e1e5b73a445fe82d40907322e1e0eec6a6745ca3cea19291c6f9f50117bb7ea \
-    --hash=sha256:0ff2b70e8e338cf13bedf146b8c29d475e2a544b5d1fe14045aee827c073842c \
-    --hash=sha256:107f4232db2172f7e8429ed7779c10f2ed16616d75ffbe77e0e0c3fcdeb51a51 \
-    --hash=sha256:14b4d06d19d39a440e72253f77067d27209c67e7611e352f79fe69e0f618f76e \
-    --hash=sha256:1b7d3a285978b27b469c0ff5fb5a72bcd69f4306dbbf22d7997d83209a8ba917 \
-    --hash=sha256:1eb7fa3b9bd9174dfe9c3b59b7a09b768ecd496debfc4976a9530a3e15c990d1 \
-    --hash=sha256:2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c \
-    --hash=sha256:28a0c5417b464562ab9842dd1fb0cc1524e60494641d973206ec24d6ec5f6909 \
-    --hash=sha256:3249011d13d0c63bea72d91cec23a9cf18c25f91d1f115121e5c9113d753fa12 \
-    --hash=sha256:44089ed06a962a3a70e96353c981d628b2d4a2f2a75ea5d90f916a62d22af2e8 \
-    --hash=sha256:4bfa291e3c931ff3c99a349d8857605dca029de61d74c6bb82bd46373959c942 \
-    --hash=sha256:50024a1ee2cf04645535c5ebaeaa0a60c5ef32e262da981f4be0546b26791950 \
-    --hash=sha256:53b72385857e04e7faca13c613c07cab411480822ac658d97fd8a4ddbaf715c8 \
-    --hash=sha256:74b7528f901f39c39cdbb50cdf08f1a2351725d9aebaef212a29abfbb06895ee \
-    --hash=sha256:7d0809e2991c9784eceeadef01c27ee6a33ca09ebba6154317a257353e3af922 \
-    --hash=sha256:896b2b80931d6b13b5d9feba3d4eebc67d5e6ec54f0cf3339d08487d55d93b0e \
-    --hash=sha256:8d9ec51cc06580f8c21b41fd3f2b3465197ba5b23c00eb7d422b7ae0380510b0 \
-    --hash=sha256:9f7a1e96fec45f70ad364e46de32ccacab4d80de238bd3c2edd036867ccd48ad \
-    --hash=sha256:ab4dc33ef0e26dc627559786a4fba0c2227f125db85d970abbf85b77506b3f51 \
-    --hash=sha256:d1e6d1f156e999edab069d79d890859806b555ce4e4da5b6418616322f0a3df1 \
-    --hash=sha256:d752bcf1b98174780e2317ada12013d612f05116456133a6acf3e17d43b71f05 \
-    --hash=sha256:e5bcc4270671936349249d26140c267397b7b4b1381f5ec8b13c53c5b53ab6e1
-greenlet==0.4.15 \
-    --hash=sha256:000546ad01e6389e98626c1367be58efa613fa82a1be98b0c6fc24b563acc6d0 \
-    --hash=sha256:0d48200bc50cbf498716712129eef819b1729339e34c3ae71656964dac907c28 \
-    --hash=sha256:23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8 \
-    --hash=sha256:37c9ba82bd82eb6a23c2e5acc03055c0e45697253b2393c9a50cef76a3985304 \
-    --hash=sha256:51503524dd6f152ab4ad1fbd168fc6c30b5795e8c70be4410a64940b3abb55c0 \
-    --hash=sha256:8041e2de00e745c0e05a502d6e6db310db7faa7c979b3a5877123548a4c0b214 \
-    --hash=sha256:81fcd96a275209ef117e9ec91f75c731fa18dcfd9ffaa1c0adbdaa3616a86043 \
-    --hash=sha256:853da4f9563d982e4121fed8c92eea1a4594a2299037b3034c3c898cb8e933d6 \
-    --hash=sha256:8b4572c334593d449113f9dc8d19b93b7b271bdbe90ba7509eb178923327b625 \
-    --hash=sha256:9416443e219356e3c31f1f918a91badf2e37acf297e2fa13d24d1cc2380f8fbc \
-    --hash=sha256:9854f612e1b59ec66804931df5add3b2d5ef0067748ea29dc60f0efdcda9a638 \
-    --hash=sha256:99a26afdb82ea83a265137a398f570402aa1f2b5dfb4ac3300c026931817b163 \
-    --hash=sha256:a19bf883b3384957e4a4a13e6bd1ae3d85ae87f4beb5957e35b0be287f12f4e4 \
-    --hash=sha256:a9f145660588187ff835c55a7d2ddf6abfc570c2651c276d3d4be8a2766db490 \
-    --hash=sha256:ac57fcdcfb0b73bb3203b58a14501abb7e5ff9ea5e2edfa06bb03035f0cff248 \
-    --hash=sha256:bcb530089ff24f6458a81ac3fa699e8c00194208a724b644ecc68422e1111939 \
-    --hash=sha256:beeabe25c3b704f7d56b573f7d2ff88fc99f0138e43480cecdfcaa3b87fe4f87 \
-    --hash=sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720 \
-    --hash=sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656
-grpcio==1.22.0 \
-    --hash=sha256:03b78b4e7dcdfe3e257bb528cc93923f9cbbab6d5babf15a60d21e9a4a70b1a2 \
-    --hash=sha256:1ce0ccfbdfe84387dbcbf44adb4ae16ec7ae70e166ffab478993eb1ea1cba3ce \
-    --hash=sha256:22e167a9406d73dd19ffe8ed6a485f17e6eac82505be8c108897f15e68badcbb \
-    --hash=sha256:31d0aeca8d8ee2301c62c5c340e0889d653b1280d68f9fa203982cb6337b050e \
-    --hash=sha256:44c7f99ca17ebbcc96fc54ed00b454d8313f1eac28c563098d8b901025aff941 \
-    --hash=sha256:5471444f53f9db6a1f1f11f5dbc173228881df8446380b6b98f90afb8fd8348e \
-    --hash=sha256:561bca3b1bde6d6564306eb05848fd155136e9c3a25d2961129b1e2edba22fce \
-    --hash=sha256:5bf58e1d2c2f55365c06e8cb5abe067b88ca2e5550fb62009c41df4b54505acf \
-    --hash=sha256:6b7163d1e85d76b0815df63fcc310daec02b44532bb433f743142d4febcb181f \
-    --hash=sha256:766d79cddad95f5f6020037fe60ea8b98578afdf0c59d5a60c106c1bdd886303 \
-    --hash=sha256:770b7372d5ca68308ff66d7baee53369fa5ce985f84bcb6aa1948c1f2f7b02f2 \
-    --hash=sha256:7ab178da777fc0f55b6aef5a755f99726e8e4b75e3903954df07b27059b54fcf \
-    --hash=sha256:8078305e77c2f6649d36b24d8778096413e474d9d7892c6f92cfb589c9d71b2e \
-    --hash=sha256:85600b63a386d860eeaa955e9335e18dd0d7e5477e9214825abf2c2884488369 \
-    --hash=sha256:857d9b939ae128be1c0c792eb885c7ff6a386b9dea899ac4b06f4d90a31f9d87 \
-    --hash=sha256:87a41630c90c179fa5c593400f30a467c498972c702f348d41e19dafeb1d319e \
-    --hash=sha256:8805d486c6128cc0fcc8ecf16c4095d99a8693a541ef851429ab334e028a4a97 \
-    --hash=sha256:8d71b7a89c306a41ccc7741fc9409b14f5b86727455c2a1c0c7cfcb0f784e1f2 \
-    --hash=sha256:9e1b80bd65f8f160880cb4dad7f55697f6d37b2d7f251fc0c2128e811928f369 \
-    --hash=sha256:9e290c84a145ae2411ee0ec9913c41cd7500e2e7485fe93632434d84ef4fda67 \
-    --hash=sha256:9ec9f88b5bc94bd99372f27cdd53af1c92ba06717380b127733b953cfb181174 \
-    --hash=sha256:a0a02a8b4ba6deadf706d5f849539b3685b72b186a3c9ef5d43e8972ed60fb6f \
-    --hash=sha256:a4059c59519f5940e01a071f74ae2a60ea8f6185b03d22a09d40c7959a36b16b \
-    --hash=sha256:a6e028c2a6da2ebfa2365a5b32531d311fbfec0e3600fc27e901b64f0ff7e54e \
-    --hash=sha256:adcdebf9f8463df4120c427cf6c9aed39258bccd03ed37b6939e7a145d64d6e0 \
-    --hash=sha256:bdec982610259d07156a58f80b8c3e69be7751a9208bc577b059c5193d087fad \
-    --hash=sha256:cefc4d4251ffb73feb303d4b7e9d6c367cb60f2db16d259ea28b114045f965aa \
-    --hash=sha256:d4145c8aa6afbac10ad27e408f7ce15992fe89ba5d0b4abca31c0c2729864c03 \
-    --hash=sha256:da76dc5ad719ee99de5ea28a5629ff92172cbb4a70d8a6ae3a5b7a53c7382ce1 \
-    --hash=sha256:dde2452c08ef8b6426ccab6b5b6de9f06d836d9937d6870e68153cbf8cb49348 \
-    --hash=sha256:e3d88091d2539a4868750914a6fe7b9ec50e42b913851fc1b77423b5bd918530 \
-    --hash=sha256:f9c67cfe6278499d7f83559dc6322a8bbb108e307817a3d7acbfea807b3603cc
-gunicorn==19.9.0 \
-    --hash=sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471 \
-    --hash=sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3
-idna==2.8 \
-    --hash=sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407 \
-    --hash=sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c
-invoke==1.2.0 \
-    --hash=sha256:4f4de934b15c2276caa4fbc5a3b8a61c0eb0b234f2be1780d2b793321995c2d6 \
-    --hash=sha256:dc492f8f17a0746e92081aec3f86ae0b4750bf41607ea2ad87e5a7b5705121b7 \
-    --hash=sha256:eb6f9262d4d25b40330fb21d1e99bf0f85011ccc3526980f8a3eaedd4b43892e
-itsdangerous==1.1.0 \
-    --hash=sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19 \
-    --hash=sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749
-jinja2==2.10.1 \
-    --hash=sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013 \
-    --hash=sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b
-markupsafe==1.1.1 \
-    --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \
-    --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \
-    --hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \
-    --hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \
-    --hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \
-    --hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \
-    --hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \
-    --hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \
-    --hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \
-    --hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \
-    --hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \
-    --hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \
-    --hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \
-    --hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \
-    --hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \
-    --hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \
-    --hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \
-    --hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \
-    --hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \
-    --hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \
-    --hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \
-    --hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \
-    --hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \
-    --hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \
-    --hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \
-    --hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \
-    --hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \
-    --hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7
-paramiko==2.4.2 \
-    --hash=sha256:3c16b2bfb4c0d810b24c40155dbfd113c0521e7e6ee593d704e84b4c658a1f3b \
-    --hash=sha256:a8975a7df3560c9f1e2b43dc54ebd40fd00a7017392ca5445ce7df409f900fcb
-protobuf==3.9.0 \
-    --hash=sha256:05c36022fef3c7d3562ac22402965c0c2b9fe8421f459bb377323598996e407f \
-    --hash=sha256:139b7eadcca0a861d60b523cb37d9475505e0dfb07972436b15407c2b968d87e \
-    --hash=sha256:15f683006cb77fb849b1f561e509b03dd2b7dcc749086b8dd1831090d0ba4740 \
-    --hash=sha256:2ad566b7b7cdd8717c7af1825e19f09e8fef2787b77fcb979588944657679604 \
-    --hash=sha256:35cfcf97642ef62108e10a9431c77733ec7eaab8e32fe4653de20403429907cb \
-    --hash=sha256:387822859ecdd012fdc25ec879f7f487da6e1d5b1ae6115e227e6be208836f71 \
-    --hash=sha256:4df14cbe1e7134afcfdbb9f058949e31c466de27d9b2f7fb4da9e0b67231b538 \
-    --hash=sha256:586c4ca37a7146d4822c700059f150ac3445ce0aef6f3ea258640838bb892dc2 \
-    --hash=sha256:58b11e530e954d29ab3180c48dc558a409f705bf16739fd4e0d3e07924ad7add \
-    --hash=sha256:63c8c98ccb8c95f41c18fb829aeeab21c6249adee4ed75354125bdc44488f30e \
-    --hash=sha256:72edcbacd0c73eef507d2ff1af99a6c27df18e66a3ff4351e401182e4de62b03 \
-    --hash=sha256:83dc8a561b3b954fd7002c690bb83278b8d1742a1e28abba9aaef28b0c8b437d \
-    --hash=sha256:913171ecc84c2726b86574e40549a0ea619d569657c5a5ff782a3be7d81401a5 \
-    --hash=sha256:aabb7c741d3416671c3e6fe7c52970a226e6a8274417a97d7d795f953fadef36 \
-    --hash=sha256:b3452bbda12b1cbe2187d416779de07b2ab4c497d83a050e43c344778763721d \
-    --hash=sha256:c5d5b8d4a9212338297fa1fa44589f69b470c0ba1d38168b432d577176b386a8 \
-    --hash=sha256:d86ee389c2c4fc3cebabb8ce83a8e97b6b3b5dc727b7419c1ccdc7b6e545a233 \
-    --hash=sha256:f2db8c754de788ab8be5e108e1e967c774c0942342b4f8aaaf14063889a6cfdc
-pyasn1==0.4.5 \
-    --hash=sha256:da2420fe13a9452d8ae97a0e478adde1dee153b11ba832a95b223a2ba01c10f7 \
-    --hash=sha256:da6b43a8c9ae93bc80e2739efb38cc776ba74a886e3e9318d65fe81a8b8a2c6e
-pycparser==2.19 \
-    --hash=sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3
-pynacl==1.3.0 \
-    --hash=sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255 \
-    --hash=sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c \
-    --hash=sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e \
-    --hash=sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae \
-    --hash=sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621 \
-    --hash=sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56 \
-    --hash=sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39 \
-    --hash=sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310 \
-    --hash=sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1 \
-    --hash=sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a \
-    --hash=sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786 \
-    --hash=sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b \
-    --hash=sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b \
-    --hash=sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f \
-    --hash=sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20 \
-    --hash=sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415 \
-    --hash=sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715 \
-    --hash=sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1 \
-    --hash=sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0
-python-dateutil==2.8.0 \
-    --hash=sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb \
-    --hash=sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e
-pytz==2019.1 \
-    --hash=sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda \
-    --hash=sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141
-requests==2.22.0 \
-    --hash=sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4 \
-    --hash=sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31
-six==1.12.0 \
-    --hash=sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c \
-    --hash=sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73
-sqlparse==0.3.0 \
-    --hash=sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177 \
-    --hash=sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873
-urllib3==1.25.3 \
-    --hash=sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1 \
-    --hash=sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232
-uwsgi==2.0.18 \
-    --hash=sha256:4972ac538800fb2d421027f49b4a1869b66048839507ccf0aa2fda792d99f583
-werkzeug==0.15.5 \
-    --hash=sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4 \
-    --hash=sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6
-
-# The following packages are considered to be unsafe in a requirements file:
-setuptools==41.0.1 \
-    --hash=sha256:a222d126f5471598053c9a77f4b5d4f26eaa1f150ad6e01dcf1a42e185d05613 \
-    --hash=sha256:c7769ce668c7a333d84e17fe8b524b1c45e7ee9f7908ad0a73e1eda7e6a5aebf \
-    # via protobuf
diff --git a/third_party/py/BUILD b/third_party/py/BUILD
new file mode 100644
index 0000000..56b5534
--- /dev/null
+++ b/third_party/py/BUILD
@@ -0,0 +1,7 @@
+load("@com_apt_itude_rules_pip//rules:lock.bzl", "pip_lock")
+
+pip_lock(
+    name = "lock",
+    requirements = ["requirements.txt"],
+    python_version = "PY3",
+)
diff --git a/third_party/py/requirements-lock.json b/third_party/py/requirements-lock.json
new file mode 100644
index 0000000..45334f9
--- /dev/null
+++ b/third_party/py/requirements-lock.json
@@ -0,0 +1,430 @@
+{
+  "environments": {
+    "linux_py3": {
+      "python_version": 3,
+      "requirements": {
+        "arrow": {
+          "dependencies": [
+            "python-dateutil"
+          ],
+          "is_direct": true,
+          "source": "arrow_0_14_5_py2_py3_none_any",
+          "version": "0.14.5"
+        },
+        "asn1crypto": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "asn1crypto_0_24_0_py2_py3_none_any",
+          "version": "0.24.0"
+        },
+        "bcrypt": {
+          "dependencies": [
+            "cffi",
+            "six"
+          ],
+          "is_direct": true,
+          "source": "bcrypt_3_1_5_cp34_abi3_manylinux1_x86_64",
+          "version": "3.1.5"
+        },
+        "certifi": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "certifi_2019_6_16_py2_py3_none_any",
+          "version": "2019.6.16"
+        },
+        "cffi": {
+          "dependencies": [
+            "pycparser"
+          ],
+          "is_direct": true,
+          "source": "cffi_1_11_5_cp36_cp36m_manylinux1_x86_64",
+          "version": "1.11.5"
+        },
+        "chardet": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "chardet_3_0_4_py2_py3_none_any",
+          "version": "3.0.4"
+        },
+        "click": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "Click_7_0_py2_py3_none_any",
+          "version": "7.0"
+        },
+        "cryptography": {
+          "dependencies": [
+            "asn1crypto",
+            "cffi",
+            "idna",
+            "six"
+          ],
+          "is_direct": true,
+          "source": "cryptography_2_4_2_cp34_abi3_manylinux1_x86_64",
+          "version": "2.4.2"
+        },
+        "django": {
+          "dependencies": [
+            "pytz",
+            "sqlparse"
+          ],
+          "is_direct": true,
+          "source": "Django_2_2_3_py3_none_any",
+          "version": "2.2.3"
+        },
+        "fabric": {
+          "dependencies": [
+            "cryptography",
+            "invoke",
+            "paramiko"
+          ],
+          "is_direct": true,
+          "source": "fabric_2_4_0_py2_py3_none_any",
+          "version": "2.4.0"
+        },
+        "flask": {
+          "dependencies": [
+            "click",
+            "itsdangerous",
+            "jinja2",
+            "werkzeug"
+          ],
+          "is_direct": true,
+          "source": "Flask_1_1_1_py2_py3_none_any",
+          "version": "1.1.1"
+        },
+        "flask-sqlalchemy": {
+          "dependencies": [
+            "flask",
+            "sqlalchemy"
+          ],
+          "is_direct": true,
+          "source": "Flask_SQLAlchemy_2_4_0_py2_py3_none_any",
+          "version": "2.4.0"
+        },
+        "future": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "future_0_17_1_py3_none_any",
+          "version": "0.17.1"
+        },
+        "gevent": {
+          "dependencies": [
+            "greenlet"
+          ],
+          "is_direct": true,
+          "source": "gevent_1_4_0_cp36_cp36m_manylinux1_x86_64",
+          "version": "1.4.0"
+        },
+        "greenlet": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "greenlet_0_4_15_cp36_cp36m_manylinux1_x86_64",
+          "version": "0.4.15"
+        },
+        "grpcio": {
+          "dependencies": [
+            "six"
+          ],
+          "is_direct": true,
+          "source": "grpcio_1_22_0_cp36_cp36m_manylinux1_x86_64",
+          "version": "1.22.0"
+        },
+        "gunicorn": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "gunicorn_19_9_0_py2_py3_none_any",
+          "version": "19.9.0"
+        },
+        "idna": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "idna_2_8_py2_py3_none_any",
+          "version": "2.8"
+        },
+        "invoke": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "invoke_1_2_0_py3_none_any",
+          "version": "1.2.0"
+        },
+        "itsdangerous": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "itsdangerous_1_1_0_py2_py3_none_any",
+          "version": "1.1.0"
+        },
+        "jinja2": {
+          "dependencies": [
+            "markupsafe"
+          ],
+          "is_direct": true,
+          "source": "Jinja2_2_10_1_py2_py3_none_any",
+          "version": "2.10.1"
+        },
+        "markupsafe": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "MarkupSafe_1_1_1_cp36_cp36m_manylinux1_x86_64",
+          "version": "1.1.1"
+        },
+        "paramiko": {
+          "dependencies": [
+            "bcrypt",
+            "cryptography",
+            "pyasn1",
+            "pynacl"
+          ],
+          "is_direct": true,
+          "source": "paramiko_2_4_2_py2_py3_none_any",
+          "version": "2.4.2"
+        },
+        "protobuf": {
+          "dependencies": [
+            "setuptools",
+            "six"
+          ],
+          "is_direct": true,
+          "source": "protobuf_3_9_0_cp36_cp36m_manylinux1_x86_64",
+          "version": "3.9.0"
+        },
+        "pyasn1": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "pyasn1_0_4_5_py2_py3_none_any",
+          "version": "0.4.5"
+        },
+        "pycparser": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "pycparser_2_19_py2_py3_none_any",
+          "version": "2.19"
+        },
+        "pynacl": {
+          "dependencies": [
+            "cffi",
+            "six"
+          ],
+          "is_direct": true,
+          "source": "PyNaCl_1_3_0_cp34_abi3_manylinux1_x86_64",
+          "version": "1.3.0"
+        },
+        "python-dateutil": {
+          "dependencies": [
+            "six"
+          ],
+          "is_direct": true,
+          "source": "python_dateutil_2_8_0_py2_py3_none_any",
+          "version": "2.8.0"
+        },
+        "pytz": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "pytz_2019_1_py2_py3_none_any",
+          "version": "2019.1"
+        },
+        "requests": {
+          "dependencies": [
+            "certifi",
+            "chardet",
+            "idna",
+            "urllib3"
+          ],
+          "is_direct": true,
+          "source": "requests_2_22_0_py2_py3_none_any",
+          "version": "2.22.0"
+        },
+        "setuptools": {
+          "dependencies": [],
+          "is_direct": false,
+          "source": "setuptools_41_2_0_py2_py3_none_any",
+          "version": "41.2.0"
+        },
+        "six": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "six_1_12_0_py2_py3_none_any",
+          "version": "1.12.0"
+        },
+        "sqlalchemy": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "SQLAlchemy_1_3_8_cp36_cp36m_linux_x86_64",
+          "version": "1.3.8"
+        },
+        "sqlparse": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "sqlparse_0_3_0_py2_py3_none_any",
+          "version": "0.3.0"
+        },
+        "urllib3": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "urllib3_1_25_3_py2_py3_none_any",
+          "version": "1.25.3"
+        },
+        "uwsgi": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "uWSGI_2_0_18_cp36_cp36m_linux_x86_64",
+          "version": "2.0.18"
+        },
+        "werkzeug": {
+          "dependencies": [],
+          "is_direct": true,
+          "source": "Werkzeug_0_15_5_py2_py3_none_any",
+          "version": "0.15.5"
+        }
+      },
+      "sys_platform": "linux"
+    }
+  },
+  "local_wheels_package": "@hscloud//third_party/py/wheels",
+  "sources": {
+    "Click_7_0_py2_py3_none_any": {
+      "sha256": "2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
+      "url": "https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl"
+    },
+    "Django_2_2_3_py3_none_any": {
+      "sha256": "6e974d4b57e3b29e4882b244d40171d6a75202ab8d2402b8e8adbd182e25cf0c",
+      "url": "https://files.pythonhosted.org/packages/39/b0/2138c31bf13e17afc32277239da53e9dfcce27bac8cb68cf1c0123f1fdf5/Django-2.2.3-py3-none-any.whl"
+    },
+    "Flask_1_1_1_py2_py3_none_any": {
+      "sha256": "45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6",
+      "url": "https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl"
+    },
+    "Flask_SQLAlchemy_2_4_0_py2_py3_none_any": {
+      "sha256": "8631bbea987bc3eb0f72b1f691d47bd37ceb795e73b59ab48586d76d75a7c605",
+      "url": "https://files.pythonhosted.org/packages/08/ca/582442cad71504a1514a2f053006c8bb128844133d6076a4df17117545fa/Flask_SQLAlchemy-2.4.0-py2.py3-none-any.whl"
+    },
+    "Jinja2_2_10_1_py2_py3_none_any": {
+      "sha256": "14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b",
+      "url": "https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl"
+    },
+    "MarkupSafe_1_1_1_cp36_cp36m_manylinux1_x86_64": {
+      "sha256": "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
+      "url": "https://files.pythonhosted.org/packages/b2/5f/23e0023be6bb885d00ffbefad2942bc51a620328ee910f64abe5a8d18dd1/MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl"
+    },
+    "PyNaCl_1_3_0_cp34_abi3_manylinux1_x86_64": {
+      "sha256": "aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415",
+      "url": "https://files.pythonhosted.org/packages/27/15/2cd0a203f318c2240b42cd9dd13c931ddd61067809fee3479f44f086103e/PyNaCl-1.3.0-cp34-abi3-manylinux1_x86_64.whl"
+    },
+    "SQLAlchemy_1_3_8_cp36_cp36m_linux_x86_64": {
+      "file": "SQLAlchemy-1.3.8-cp36-cp36m-linux_x86_64.whl"
+    },
+    "Werkzeug_0_15_5_py2_py3_none_any": {
+      "sha256": "87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4",
+      "url": "https://files.pythonhosted.org/packages/d1/ab/d3bed6b92042622d24decc7aadc8877badf18aeca1571045840ad4956d3f/Werkzeug-0.15.5-py2.py3-none-any.whl"
+    },
+    "arrow_0_14_5_py2_py3_none_any": {
+      "sha256": "a12de0124d812d15061ed36c7eb4a421fa1b95026a502a0b2062e9ea00fc4446",
+      "url": "https://files.pythonhosted.org/packages/4f/c6/32df2c68e02e2d6b4457223fa499634edabb2d4ff74f00087ffff49b4be4/arrow-0.14.5-py2.py3-none-any.whl"
+    },
+    "asn1crypto_0_24_0_py2_py3_none_any": {
+      "sha256": "2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87",
+      "url": "https://files.pythonhosted.org/packages/ea/cd/35485615f45f30a510576f1a56d1e0a7ad7bd8ab5ed7cdc600ef7cd06222/asn1crypto-0.24.0-py2.py3-none-any.whl"
+    },
+    "bcrypt_3_1_5_cp34_abi3_manylinux1_x86_64": {
+      "sha256": "efcaace6e2915434d84e865c44f0cfe34e802269378afbb39a4aa6381aaec78b",
+      "url": "https://files.pythonhosted.org/packages/31/4b/4057d0716e7170c29ff12e19791eb6037422620835e4a58a01d4790e56d1/bcrypt-3.1.5-cp34-abi3-manylinux1_x86_64.whl"
+    },
+    "certifi_2019_6_16_py2_py3_none_any": {
+      "sha256": "046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
+      "url": "https://files.pythonhosted.org/packages/69/1b/b853c7a9d4f6a6d00749e94eb6f3a041e342a885b87340b79c1ef73e3a78/certifi-2019.6.16-py2.py3-none-any.whl"
+    },
+    "cffi_1_11_5_cp36_cp36m_manylinux1_x86_64": {
+      "sha256": "770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc",
+      "url": "https://files.pythonhosted.org/packages/6d/c0/47db8f624f3e4e2f3f27be03a93379d1ba16a1450a7b1aacfa0366e2c0dd/cffi-1.11.5-cp36-cp36m-manylinux1_x86_64.whl"
+    },
+    "chardet_3_0_4_py2_py3_none_any": {
+      "sha256": "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691",
+      "url": "https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl"
+    },
+    "cryptography_2_4_2_cp34_abi3_manylinux1_x86_64": {
+      "sha256": "70596e90398574b77929cd87e1ac6e43edd0e29ba01e1365fed9c26bde295aa5",
+      "url": "https://files.pythonhosted.org/packages/60/c7/99b33c53cf3f20a97a4c4bfd3ab66dcc93d99da0a97cc9597aa36ae6bb62/cryptography-2.4.2-cp34-abi3-manylinux1_x86_64.whl"
+    },
+    "fabric_2_4_0_py2_py3_none_any": {
+      "sha256": "98538f2f3f63cf52497a8d0b24d18424ae83fe67ac7611225c72afb9e67f2cf6",
+      "url": "https://files.pythonhosted.org/packages/d9/e4/e6fa248c94ee5d45def54b609fcf70f39d0b7f7050f2d4405c5f156b5516/fabric-2.4.0-py2.py3-none-any.whl"
+    },
+    "future_0_17_1_py3_none_any": {
+      "file": "future-0.17.1-py3-none-any.whl"
+    },
+    "gevent_1_4_0_cp36_cp36m_manylinux1_x86_64": {
+      "sha256": "2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c",
+      "url": "https://files.pythonhosted.org/packages/f2/ca/5b5962361ed832847b6b2f9a2d0452c8c2f29a93baef850bb8ad067c7bf9/gevent-1.4.0-cp36-cp36m-manylinux1_x86_64.whl"
+    },
+    "greenlet_0_4_15_cp36_cp36m_manylinux1_x86_64": {
+      "sha256": "23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8",
+      "url": "https://files.pythonhosted.org/packages/bf/45/142141aa47e01a5779f0fa5a53b81f8379ce8f2b1cd13df7d2f1d751ae42/greenlet-0.4.15-cp36-cp36m-manylinux1_x86_64.whl"
+    },
+    "grpcio_1_22_0_cp36_cp36m_manylinux1_x86_64": {
+      "sha256": "561bca3b1bde6d6564306eb05848fd155136e9c3a25d2961129b1e2edba22fce",
+      "url": "https://files.pythonhosted.org/packages/f2/5d/b434403adb2db8853a97828d3d19f2032e79d630e0d11a8e95d243103a11/grpcio-1.22.0-cp36-cp36m-manylinux1_x86_64.whl"
+    },
+    "gunicorn_19_9_0_py2_py3_none_any": {
+      "sha256": "aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471",
+      "url": "https://files.pythonhosted.org/packages/8c/da/b8dd8deb741bff556db53902d4706774c8e1e67265f69528c14c003644e6/gunicorn-19.9.0-py2.py3-none-any.whl"
+    },
+    "idna_2_8_py2_py3_none_any": {
+      "sha256": "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c",
+      "url": "https://files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl"
+    },
+    "invoke_1_2_0_py3_none_any": {
+      "sha256": "4f4de934b15c2276caa4fbc5a3b8a61c0eb0b234f2be1780d2b793321995c2d6",
+      "url": "https://files.pythonhosted.org/packages/be/9f/8508712c9cad73ac0c8eeb2c3e51c9ef65136653dda2b512bde64109f023/invoke-1.2.0-py3-none-any.whl"
+    },
+    "itsdangerous_1_1_0_py2_py3_none_any": {
+      "sha256": "b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749",
+      "url": "https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl"
+    },
+    "paramiko_2_4_2_py2_py3_none_any": {
+      "sha256": "3c16b2bfb4c0d810b24c40155dbfd113c0521e7e6ee593d704e84b4c658a1f3b",
+      "url": "https://files.pythonhosted.org/packages/cf/ae/94e70d49044ccc234bfdba20114fa947d7ba6eb68a2e452d89b920e62227/paramiko-2.4.2-py2.py3-none-any.whl"
+    },
+    "protobuf_3_9_0_cp36_cp36m_manylinux1_x86_64": {
+      "sha256": "2ad566b7b7cdd8717c7af1825e19f09e8fef2787b77fcb979588944657679604",
+      "url": "https://files.pythonhosted.org/packages/dc/0e/e7cdff89745986c984ba58e6ff6541bc5c388dd9ab9d7d312b3b1532584a/protobuf-3.9.0-cp36-cp36m-manylinux1_x86_64.whl"
+    },
+    "pyasn1_0_4_5_py2_py3_none_any": {
+      "sha256": "da6b43a8c9ae93bc80e2739efb38cc776ba74a886e3e9318d65fe81a8b8a2c6e",
+      "url": "https://files.pythonhosted.org/packages/7b/7c/c9386b82a25115cccf1903441bba3cbadcfae7b678a20167347fa8ded34c/pyasn1-0.4.5-py2.py3-none-any.whl"
+    },
+    "pycparser_2_19_py2_py3_none_any": {
+      "file": "pycparser-2.19-py2.py3-none-any.whl"
+    },
+    "python_dateutil_2_8_0_py2_py3_none_any": {
+      "sha256": "7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
+      "url": "https://files.pythonhosted.org/packages/41/17/c62faccbfbd163c7f57f3844689e3a78bae1f403648a6afb1d0866d87fbb/python_dateutil-2.8.0-py2.py3-none-any.whl"
+    },
+    "pytz_2019_1_py2_py3_none_any": {
+      "sha256": "303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda",
+      "url": "https://files.pythonhosted.org/packages/3d/73/fe30c2daaaa0713420d0382b16fbb761409f532c56bdcc514bf7b6262bb6/pytz-2019.1-py2.py3-none-any.whl"
+    },
+    "requests_2_22_0_py2_py3_none_any": {
+      "sha256": "9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31",
+      "url": "https://files.pythonhosted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/requests-2.22.0-py2.py3-none-any.whl"
+    },
+    "setuptools_41_2_0_py2_py3_none_any": {
+      "sha256": "4380abcf2a4ffd1a5ba22d687c6d690dce83b2b51c70e9c6d09f7e8c7e8040dc",
+      "url": "https://files.pythonhosted.org/packages/b2/86/095d2f7829badc207c893dd4ac767e871f6cd547145df797ea26baea4e2e/setuptools-41.2.0-py2.py3-none-any.whl"
+    },
+    "six_1_12_0_py2_py3_none_any": {
+      "sha256": "3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+      "url": "https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl"
+    },
+    "sqlparse_0_3_0_py2_py3_none_any": {
+      "sha256": "40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177",
+      "url": "https://files.pythonhosted.org/packages/ef/53/900f7d2a54557c6a37886585a91336520e5539e3ae2423ff1102daf4f3a7/sqlparse-0.3.0-py2.py3-none-any.whl"
+    },
+    "uWSGI_2_0_18_cp36_cp36m_linux_x86_64": {
+      "file": "uWSGI-2.0.18-cp36-cp36m-linux_x86_64.whl"
+    },
+    "urllib3_1_25_3_py2_py3_none_any": {
+      "sha256": "b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
+      "url": "https://files.pythonhosted.org/packages/e6/60/247f23a7121ae632d62811ba7f273d0e58972d75e58a94d329d51550a47d/urllib3-1.25.3-py2.py3-none-any.whl"
+    }
+  }
+}
\ No newline at end of file
diff --git a/pip/requirements.in b/third_party/py/requirements.txt
similarity index 89%
rename from pip/requirements.in
rename to third_party/py/requirements.txt
index d3bf3ce..3722ff7 100644
--- a/pip/requirements.in
+++ b/third_party/py/requirements.txt
@@ -6,9 +6,10 @@
 chardet==3.0.4
 Click==7.0
 cryptography==2.4.2
-Django==2.2.3
+django==2.2.3
 fabric==2.4.0
 Flask==1.1.1
+Flask-SQLAlchemy==2.4.0
 future==0.17.1
 gevent==1.4.0
 greenlet==0.4.15
@@ -28,6 +29,7 @@
 pytz==2019.1
 requests==2.22.0
 six==1.12.0
+SQLAlchemy==1.3.8
 sqlparse==0.3.0
 urllib3==1.25.3
 uWSGI==2.0.18
diff --git a/third_party/py/wheels/BUILD b/third_party/py/wheels/BUILD
new file mode 100644
index 0000000..1b09257
--- /dev/null
+++ b/third_party/py/wheels/BUILD
@@ -0,0 +1 @@
+# This is a generated file which may be overwritten with a custom BUILD file
\ No newline at end of file
diff --git a/third_party/py/wheels/SQLAlchemy-1.3.8-cp36-cp36m-linux_x86_64.whl b/third_party/py/wheels/SQLAlchemy-1.3.8-cp36-cp36m-linux_x86_64.whl
new file mode 100644
index 0000000..b54494b
--- /dev/null
+++ b/third_party/py/wheels/SQLAlchemy-1.3.8-cp36-cp36m-linux_x86_64.whl
Binary files differ
diff --git a/third_party/py/wheels/future-0.17.1-py3-none-any.whl b/third_party/py/wheels/future-0.17.1-py3-none-any.whl
new file mode 100644
index 0000000..77f89e3
--- /dev/null
+++ b/third_party/py/wheels/future-0.17.1-py3-none-any.whl
Binary files differ
diff --git a/third_party/py/wheels/pycparser-2.19-py2.py3-none-any.whl b/third_party/py/wheels/pycparser-2.19-py2.py3-none-any.whl
new file mode 100644
index 0000000..f00822f
--- /dev/null
+++ b/third_party/py/wheels/pycparser-2.19-py2.py3-none-any.whl
Binary files differ
diff --git a/third_party/py/wheels/uWSGI-2.0.18-cp36-cp36m-linux_x86_64.whl b/third_party/py/wheels/uWSGI-2.0.18-cp36-cp36m-linux_x86_64.whl
new file mode 100644
index 0000000..1bf3680
--- /dev/null
+++ b/third_party/py/wheels/uWSGI-2.0.18-cp36-cp36m-linux_x86_64.whl
Binary files differ