Merge changes Ie974e7e8,I0bda7f6e

* changes:
  app/covid-formity: add kurjerzy integration
  app/covid-formity: image update, add /qr1, /manual, /video redirect
diff --git a/.bazelrc b/.bazelrc
index 03cd77d..419641e 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -2,6 +2,7 @@
 build --host_force_python=PY2
 test --host_force_python=PY2
 run --host_force_python=PY2
+build --stamp
 build --workspace_status_command=./bzl/workspace-status.sh
 test --build_tests_only
 test --test_output=errors
diff --git a/BUILD b/BUILD
index b29b1c5..1264f0a 100644
--- a/BUILD
+++ b/BUILD
@@ -1,5 +1,4 @@
 # Gazelle settings
-
 load("@bazel_gazelle//:def.bzl", "gazelle")
 
 # gazelle:prefix code.hackerspace.pl/hscloud
diff --git a/COPYING b/COPYING
index a8c79dd..590d418 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,10 @@
-Copyright 2018 Serge Bazanski
+The contents of the hscloud repository are, unless stated otherwise in respestive files or subdirectories, distributed under the ISC License. To find all authors of the code for the purposes of copyright, please consult the repository's Git history or run the following command:
+
+    git log --pretty=format:"%an" | sort | uniq
+
+ISC License:
+
+Copyright 2018-2020 HSCloud Authors
 
 Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
 
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..559b342
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,4 @@
+owners:
+- q3k
+- informatic
+- implr
diff --git a/README b/README
deleted file mode 100644
index dc389bd..0000000
--- a/README
+++ /dev/null
@@ -1,24 +0,0 @@
-HSCloud
-=======
-
-This is a monorepo. You'll need bash and Bazel 1.0.0+ to use it.
-
-If you have Nix installed you will also be able to manage bare metal nodes. If you don't want that, you can skip it.
-
-
-Getting started
----------------
-
-    cd hscloud
-    . env.sh # setup PATH and hscloud_root
-    tools/install.sh # build tools
-
-
-Then, to get Kubernetes access to k0.hswaw.net (current nearly-production cluster):
-
-    prodaccess
-    kubectl version
-
-You will automatically get a `personal-$USERNAME` namespace created in which you have full admin rights.
-
-For mor information about the cluster, see [cluster/README].
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8c04fe4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+![](doc/img/hscloud-smol.png)
+
+`hscloud` is the main monorepo of the Warsaw Hackerspace infrastructure code.
+
+Any time you see a `//path/like/this`, it refers to the root of hscloud, ie. the path `path/like/this` in this repository. Perforce and/or Bazel users should feel right at home.
+
+
+Viewing this documentation
+--------------------------
+
+For a pleaseant web viewing experience, [see this documentation in hackdoc](https://hackdoc.hackerspace.pl/). This will allow you to read this markdown file (and others) in a pretty, linkable view.
+
+Getting started
+---------------
+
+See [//doc/codelabs](/doc/codelabs) for tutorials on how to use hscloud.
+
+If you want to browse the source of `hscloud` in a web browser, use [cs.hackerspace.pl](https://cs.hackerspace.pl/hscloud).
+
+If you want some other help, talk to q3k, informatic or your therapist.
+
+Directory Structure
+-------------------
+
+Directories you should care about:
+
+ - **app**: external services that we host that are somewhat universal: matrix, covid-formity, etc.
+ - **bgpwtf**: code related to our little ISP
+ - **cluster**: code related to our Kubernetes cluster (`k0.hswaw.net`)
+ - **dc**: code related to datacenter automation
+ - **devtools**: code related to developer tooling, like gerrit or hackdoc
+ - **doc**: high-level documentation that doesn't fit anywhere else, ie. codelabs
+ - **hswaw**: Warsaw Hackerspace specific/internal services. The line between this and **app** is unfortunately blurry.
+ - **personal**: user's personal (experimental) directories
+ - **kube**, **go**: code specific to languages but general to the whole of hscloud
+
+Licensing
+---------
+
+Unless noted otherwise, code in hscloud is licensed under the BSD 0-clause license - see [COPYING](/COPYING).
diff --git a/WORKSPACE b/WORKSPACE
index 9188d92..6ecd658 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -5,33 +5,113 @@
 load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
-# Skylib
-
-skylib_version = "0.8.0"
-
+# Protobuf deps (shared between many rules).
+# Load this as early as possible, to avoid a different version being pulled in by deps of something else
 http_archive(
-    name = "bazel_skylib",
-    type = "tar.gz",
-    url = "https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib.{}.tar.gz".format(skylib_version, skylib_version),
-    sha256 = "2ef429f5d7ce7111263289644d233707dba35e39696377ebab8b0bc701f7818e",
+    name = "com_google_protobuf",
+    sha256 = "bb8ce9ba11eb7bccf080599fe7cad9cc461751c8dd1ba61701c0070d58cde973",
+    strip_prefix = "protobuf-3.12.2",
+    urls = ["https://github.com/google/protobuf/archive/v3.12.2.tar.gz"],
 )
 
-# subpar
+load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
+protobuf_deps()
 
-git_repository(
-    name = "subpar",
-    remote = "https://github.com/q3k/subpar",
-    commit = "5dd9fb4586616c69df9b3f5aba12f08f85d708d1",
-    shallow_since = "1563277890 +0200",
+# Force rules_python at a bleeding edge version (for pip3_import).
+http_archive(
+    name = "rules_python",
+    url = "https://github.com/bazelbuild/rules_python/releases/download/0.0.3/rules_python-0.0.3.tar.gz",
+    sha256 = "e46612e9bb0dae8745de6a0643be69e8665a03f63163ac6610c210e80d14c3e4",
 )
 
+# Load and setup Nixpkgs, if Nix is present on the build system.
+http_archive(
+    name = "io_tweag_rules_nixpkgs",
+    strip_prefix = "rules_nixpkgs-dc24090573d74adcf38730422941fd69b87682c7",
+    urls = ["https://github.com/tweag/rules_nixpkgs/archive/dc24090573d74adcf38730422941fd69b87682c7.tar.gz"],
+    sha256 = "aca86baa64174478c57f74ed09d5c2313113abe94aa3af030486d1b14032d3ed",
+)
+load("//third_party/nix:repository_rules.bzl", "hscloud_setup_nix")
+hscloud_setup_nix(
+    revision = "1179841f9a88b8a548f4b11d1a03aa25a790c379",
+    sha256 = "8b64041bfb9760de9e797c0a985a4830880c21732489f397e217d877edd9a990",
+)
+
+# Download Go/Gazelle rules
+http_archive(
+    name = "io_bazel_rules_go",
+    sha256 = "6a68e269802911fa419abb940c850734086869d7fe9bc8e12aaf60a09641c818",
+    urls = [
+        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.23.0/rules_go-v0.23.0.tar.gz",
+        "https://github.com/bazelbuild/rules_go/releases/download/v0.23.0/rules_go-v0.23.0.tar.gz",
+    ],
+)
+http_archive(
+    name = "bazel_gazelle",
+    sha256 = "bfd86b3cbe855d6c16c6fce60d76bd51f5c8dbc9cfcaef7a2bb5c1aafd0710e8",
+    urls = [
+        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.0/bazel-gazelle-v0.21.0.tar.gz",
+        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.0/bazel-gazelle-v0.21.0.tar.gz",
+    ],
+)
+
+# Python rules
+# Important: rules_python must be loaded before protobuf (and grpc) because they load an older version otherwise
+load("@rules_python//python:repositories.bzl", "py_repositories")
+py_repositories()
+
+load("@rules_python//python:pip.bzl", "pip_repositories")
+pip_repositories()
+
+load("@hscloud_pip_imports//:imports.bzl", "hscloud_pip3_import")
+hscloud_pip3_import(
+   name = "pydeps",
+   requirements = "//third_party/py:requirements.txt",
+)
+
+load("@pydeps//:requirements.bzl", "pip_install")
+pip_install()
+
+
+# Setup Go toolchain.
+# This workspace is generated by hscloud_setup_nixpkgs. It will either call
+# go_register_toolchains() to automagically get Go toolchains from the Internet
+# or, if nix is present, instead setup a toolchain from nixpkgs.
+load("@hscloud_go_toolchain//:imports.bzl", "hscloud_go_register_toolchains")
+hscloud_go_register_toolchains()
+
+# IMPORTANT: match protobuf version above with the one loaded by grpc
+http_archive(
+    name = "com_github_grpc_grpc",
+    sha256 = "419dba362eaf8f1d36849ceee17c3e2ff8ff12ac666b42d3ff02a164ebe090e9",
+    strip_prefix = "grpc-1.30.0",
+    urls = ["https://github.com/grpc/grpc/archive/v1.30.0.tar.gz"],
+)
+
+# Load grpc deps after Go, to prevent overriding Go toolchains/SDK.
+load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")
+grpc_deps()
+load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps")
+grpc_extra_deps()
+
+load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies")
+go_rules_dependencies()
+
+# gazelle:repository_macro third_party/go/repositories.bzl%go_repositories
+load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
+gazelle_dependencies()
+
+# Load Go third-party packages.
+load("//third_party/go:repositories.bzl", "go_repositories")
+go_repositories()
+
 # Docker rules
 
 http_archive(
     name = "io_bazel_rules_docker",
-    sha256 = "87fc6a2b128147a0a3039a2fd0b53cc1f2ed5adb8716f50756544a572999ae9a",
-    strip_prefix = "rules_docker-0.8.1",
-    urls = ["https://github.com/bazelbuild/rules_docker/archive/v0.8.1.tar.gz"],
+    sha256 = "dc97fccceacd4c6be14e800b2a00693d5e8d07f69ee187babfd04a80a9f8e250",
+    strip_prefix = "rules_docker-0.14.1",
+    urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.14.1/rules_docker-v0.14.1.tar.gz"],
 )
 
 load("@io_bazel_rules_docker//toolchains/docker:toolchain.bzl", docker_toolchain_configure = "toolchain_configure")
@@ -47,72 +127,8 @@
     "@io_bazel_rules_docker//repositories:repositories.bzl",
     container_repositories = "repositories",
 )
-
 container_repositories()
 
-# Nix rules
-http_archive(
-    name = "io_tweag_rules_nixpkgs",
-    strip_prefix = "rules_nixpkgs-33c50ba64c11dddb95823d12f6b1324083cc5c43",
-    urls = ["https://github.com/tweag/rules_nixpkgs/archive/33c50ba64c11dddb95823d12f6b1324083cc5c43.tar.gz"],
-    sha256 = "91fedd5151bbd9ef89efc39e2172921bd7036c68cff54712a5df8ddf62bd6922",
-)
-
-# Nix packages
-
-load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", "nixpkgs_git_repository", "nixpkgs_package")
-
-nixpkgs_git_repository(
-    name = "nixpkgs",
-    revision = "2f1f9a9fe8a3c22f0677733523eaf6bd33995d50",
-)
-
-nixpkgs_package(
-    name = "nixops",
-    attribute_path = "nixops",
-    repositories = {"nixpkgs": "@nixpkgs"},
-)
-
-# Python rules
-
-git_repository(
-    name = "com_apt_itude_rules_pip",
-    commit = "186bade4f054c0a9be7037585584c9c572cba483",
-    remote = "https://github.com/apt-itude/rules_pip.git",
-)
-
-# Python dependencies
-load("@com_apt_itude_rules_pip//rules:dependencies.bzl", "pip_rules_dependencies")
-
-pip_rules_dependencies()
-
-load("@com_apt_itude_rules_pip//rules:repository.bzl", "pip_repository")
-
-pip_repository(
-    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",
-    urls = ["https://github.com/stackb/rules_proto/archive/b93b544f851fdcd3fc5c3d47aee3b7ca158a8841.tar.gz"],
-    sha256 = "c62f0b442e82a6152fcd5b1c0b7c4028233a9e314078952b6b04253421d56d61",
-    strip_prefix = "rules_proto-b93b544f851fdcd3fc5c3d47aee3b7ca158a8841",
-)
-
-load("@build_stack_rules_proto//python:deps.bzl", "python_grpc_compile")
-
-python_grpc_compile()
-
-load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")
-
-grpc_deps()
-
 # Docker base images
 
 load("@io_bazel_rules_docker//container:container.bzl", "container_pull")
@@ -126,1818 +142,172 @@
 )
 
 container_pull(
-    name = "gerrit-3.0.0",
+    name = "gerrit-3.0.8",
     registry = "index.docker.io",
     repository = "gerritcodereview/gerrit",
-    tag = "3.0.0-ubuntu18",
-    digest = "sha256:f107729011d8b81611e35a0ad452f21a424c1820664e9f95d135ad411e87b9bb",
+    tag = "3.0.8-ubuntu18",
+    digest = "sha256:8f58236129e6547d92502a2e9d8f40129f45f15007beaeafb59fed4faffddb3e",
 )
 
-# HTTP stuff from the Internet
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
+# third_party/factorio
+load("//third_party/factorio:factorio.bzl", "factorio_repositories")
+factorio_repositories()
 
-http_file(
-    name = "factorio-headless-0.16.51",
-    urls = ["https://factorio.com/get-download/0.16.51/headless/linux64"],
-    sha256 = "6cb09f5ac87f16f8d5b43cef26c0ae26cc46a57a0382e253dfda032dc5bb367f",
-    downloaded_file_path = "factorio.tar.xz",
-)
-
-http_file(
-    name = "factorio-headless-0.17.41",
-    urls = ["https://factorio.com/get-download/0.17.41/headless/linux64"],
-    sha256 = "bf2d16b23c3bbd97e41889d3e27670b6d958fa3d50f0befb41d234f735e8e6d1",
-    downloaded_file_path = "factorio.tar.xz",
-)
-
-http_file(
-    name = "factorio-headless-0.17.52",
-    urls = ["https://factorio.com/get-download/0.17.52/headless/linux64"],
-    sha256 = "24458a4e16875b0b63677b7e7a068ce2e5b298c110381d17c6f596fd1406db0e",
-    downloaded_file_path = "factorio.tar.xz",
-)
-
-http_file(
-    name = "factorio-headless-0.17.79",
-    urls = ["https://factorio.com/get-download/0.17.79/headless/linux64"],
-    sha256 = "9ace12fa986df028dc1851bf4de2cb038044d743e98823bc1c48ba21aa4d23df",
-    downloaded_file_path = "factorio.tar.xz",
-)
-
-http_file(
-    name = "factorio-headless-0.18.12",
-    urls = ["https://factorio.com/get-download/0.18.12/headless/linux64"],
-    sha256 = "e0c6a46d66cfc02cba294a5fd34265e7e7a5168b8c8a7b16ad8dbac31470ed33",
-    downloaded_file_path = "factorio.tar.xz",
-)
-
-# Go rules
-
-http_archive(
-    name = "io_bazel_rules_go",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.21.3/rules_go-v0.21.3.tar.gz",
-        "https://github.com/bazelbuild/rules_go/releases/download/v0.21.3/rules_go-v0.21.3.tar.gz",
-    ],
-    sha256 = "af04c969321e8f428f63ceb73463d6ea817992698974abeff0161e069cd08bd6",
-)
-
-# Invoke go_rules_dependencies depending on host platform.
-load("//tools:go_sdk.bzl", "gen_imports")
-gen_imports(name = "go_sdk_imports")
-load("@go_sdk_imports//:imports.bzl", "load_go_sdk")
-load_go_sdk()
-
-# Go Gazelle rules
-
-http_archive(
-    name = "bazel_gazelle",
-    urls = [
-        "https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/bazel-gazelle/releases/download/v0.20.0/bazel-gazelle-v0.20.0.tar.gz",
-        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.20.0/bazel-gazelle-v0.20.0.tar.gz",
-    ],
-    sha256 = "d8c45ee70ec39a57e7a05e5027c32b1576cc7f16d9dd37135b0eddde45cf1b10",
-)
-
-load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
-
-gazelle_dependencies()
-
-# For devtools/gerrit/gerrit-oauth-provider
+# For devtools/gerrit/gerrit-oauth-provider and gerrit OWNERS plugin
 
 git_repository(
     name = "com_googlesource_gerrit_bazlets",
     remote = "https://gerrit.googlesource.com/bazlets",
-    commit = "8528a0df69dadf6311d8d3f81c1b693afda8bcf1",
-    shallow_since = "1560842141 +0200",
+    commit = "1d381f01c853e2c02ae35430a8e294e485635d62",
+    shallow_since = "1559431096 -0400"
 )
 
-load(
-    "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
-    "gerrit_api",
-)
-
+load("@com_googlesource_gerrit_bazlets//:gerrit_api.bzl", "gerrit_api")
 gerrit_api()
 
-load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", "maven_jar")
+load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", gerrit_maven_jar="maven_jar", GERRIT="GERRIT")
+PROLOG_VERS = "1.4.3"
+JACKSON_VER = "2.9.7"
 
-maven_jar(
+gerrit_maven_jar(
     name = "scribe",
     artifact = "org.scribe:scribe:1.3.7",
     sha1 = "583921bed46635d9f529ef5f14f7c9e83367bc6e",
 )
 
-maven_jar(
+gerrit_maven_jar(
     name = "commons-codec",
     artifact = "commons-codec:commons-codec:1.4",
     sha1 = "4216af16d38465bbab0f3dff8efa14204f7a399a",
 )
 
-# For devtools/bazel-cache
+gerrit_maven_jar(
+    name = "jackson-core",
+    artifact = "com.fasterxml.jackson.core:jackson-core:" + JACKSON_VER,
+    sha1 = "4b7f0e0dc527fab032e9800ed231080fdc3ac015",
+)
+gerrit_maven_jar(
+    name = "jackson-databind",
+    artifact = "com.fasterxml.jackson.core:jackson-databind:" + JACKSON_VER,
+    sha1 = "e6faad47abd3179666e89068485a1b88a195ceb7",
+)
+gerrit_maven_jar(
+    name = "jackson-annotations",
+    artifact = "com.fasterxml.jackson.core:jackson-annotations:" + JACKSON_VER,
+    sha1 = "4b838e5c4fc17ac02f3293e9a558bb781a51c46d",
+)
+gerrit_maven_jar(
+    name = "jackson-dataformat-yaml",
+    artifact = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:" + JACKSON_VER,
+    sha1 = "a428edc4bb34a2da98a50eb759c26941d4e85960",
+)
+gerrit_maven_jar(
+    name = "snakeyaml",
+    artifact = "org.yaml:snakeyaml:1.23",
+    sha1 = "ec62d74fe50689c28c0ff5b35d3aebcaa8b5be68",
+)
+
+gerrit_maven_jar(
+    name = "prolog-runtime",
+    artifact = "com.googlecode.prolog-cafe:prolog-runtime:" + PROLOG_VERS,
+    attach_source = False,
+    repository = GERRIT,
+    sha1 = "d5206556cbc76ffeab21313ffc47b586a1efbcbb",
+)
+gerrit_maven_jar(
+    name = "prolog-compiler",
+    artifact = "com.googlecode.prolog-cafe:prolog-compiler:" + PROLOG_VERS,
+    attach_source = False,
+    repository = GERRIT,
+    sha1 = "f37032cf1dec3e064427745bc59da5a12757a3b2",
+)
+gerrit_maven_jar(
+    name = "prolog-io",
+    artifact = "com.googlecode.prolog-cafe:prolog-io:" + PROLOG_VERS,
+    attach_source = False,
+    repository = GERRIT,
+    sha1 = "d02b2640b26f64036b6ba2b45e4acc79281cea17",
+)
+
+# minecraft spigot/bukkit deps
+# this uses rules_jvm_external vs gerrit's maven_jar because we need SNAPSHOT support
+
+http_archive(
+    name = "io_grpc_grpc_java",
+    sha256 = "446ad7a2e85bbd05406dbf95232c7c49ed90de83b3b60cb2048b0c4c9f254d29",
+    strip_prefix = "grpc-java-1.29.0",
+    url = "https://github.com/grpc/grpc-java/archive/v1.29.0.zip",
+)
+
+RULES_JVM_EXTERNAL_TAG = "3.0"
+RULES_JVM_EXTERNAL_SHA = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a"
+
+http_archive(
+    name = "rules_jvm_external",
+    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
+    sha256 = RULES_JVM_EXTERNAL_SHA,
+    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
+)
+
+load("@rules_jvm_external//:defs.bzl", "maven_install")
+load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS")
+load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS")
+
+maven_install(
+    artifacts = [
+        "org.spigotmc:spigot-api:1.15.2-R0.1-SNAPSHOT",
+        "io.grpc:grpc-netty-shaded:1.29.0",
+        "io.grpc:grpc-services:1.29.0",
+    ]  + IO_GRPC_GRPC_JAVA_ARTIFACTS,
+    generate_compat_repositories = True,
+    override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS,
+    repositories = [
+        "https://hub.spigotmc.org/nexus/content/repositories/snapshots",
+        "https://oss.sonatype.org/content/repositories/snapshots",
+        "https://repo1.maven.org/maven2/",
+    ],
+    maven_install_json = "//third_party/java:maven_install.json",
+)
+
+load("@maven//:defs.bzl", "pinned_maven_install")
+pinned_maven_install()
+
+load("@maven//:compat.bzl", "compat_repositories")
+compat_repositories()
+
+load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories")
+grpc_java_repositories()
+
+# Gerrit OWNERS plugins external repositories
 
 git_repository(
-    name = "com_github_buchgr_bazel_remote",
-    remote = "https://github.com/buchgr/bazel-remote.git",
-    commit = "a9374e638411da72a2ef2a83f490e61e2d74a976",
+    name = "com_googlesource_gerrit_plugin_owners",
+    remote = "https://gerrit.googlesource.com/plugins/owners/",
+    commit = "5e691e87b8c00a04d261a8dd313f4d16c54797e8",
+    shallow_since = "1559729722 +0900"
 )
 
 # Go image repos for Docker
 
-load(
-    "@io_bazel_rules_docker//go:image.bzl",
-    go_image_repositories = "repositories",
-)
-
+load("@io_bazel_rules_docker//go:image.bzl", go_image_repositories = "repositories")
 go_image_repositories()
-# Go repositories
 
-go_repository(
-    name = "io_k8s_kubernetes",
-    importpath = "k8s.io/kubernetes",
-    # Get from HTTP instead, this repository is _big_
-    urls = ["https://github.com/kubernetes/kubernetes/archive/v1.16.0.tar.gz"],
-    sha256 = "a8b2ee84ce38fa14404d7e56daa87aa2f2fb13e0114fb1150f294c992ab3f36c",
-    strip_prefix = "kubernetes-1.16.0",
-)
+# oniguruma, with build from //third_party/oniguruma
 
-go_repository(
-    name = "io_k8s_repo_infra",
-    commit = "df02ded38f9506e5bbcbf21702034b4fef815f2f",
-    importpath = "k8s.io/repo-infra",
+http_archive(
+    name = "com_github_kkos_oniguruma",
+    urls = ["https://github.com/kkos/oniguruma/releases/download/v6.9.5_rev1/onig-6.9.5_rev1.tar.gz"],
+    strip_prefix = "onig-6.9.5",
+    sha256 = "d33c849d1672af227944878cefe0a8fcf26fc62bedba32aa517f2f63c314a99e",
+    build_file = "@hscloud//third_party/oniguruma:BUILD.external",
 )
 
-go_repository(
-    name = "com_github_bitnami_kubecfg",
-    importpath = "github.com/bitnami/kubecfg",
-    vcs = "git",
-    commit = "dddf366990f581132cd046c723d73a2357de2dc8",
-    remote = "https://github.com/q3k/kubecfg",
-)
+# jq, with build from //third_party/jq
 
-go_repository(
-    name = "com_github_projectcalico_calicoctl",
-    importpath = "github.com/projectcalico/calicoctl",
-    # This fork implements explicit Bazel rules
-    remote = "https://github.com/q3k/calicoctl",
-    vcs = "git",
-    commit = "1bc31862f07e7539ca493de9137ed1ad56cc9f43",
-    build_file_generation = "off",
-)
-
-go_repository(
-    name = "com_github_shirou_gopsutil",
-    commit = "2cbc9195c892b304060269ef280375236d2fcac9",
-    importpath = "github.com/shirou/gopsutil",
-)
-
-go_repository(
-    name = "com_github_cloudflare_cfssl",
-    commit = "768cd563887febaad559b511aaa5964823ccb4ab",
-    importpath = "github.com/cloudflare/cfssl",
-)
-
-go_repository(
-    name = "com_github_mattn_go_sqlite3",
-    commit = "5994cc52dfa89a4ee21ac891b06fbc1ea02c52d3",
-    importpath = "github.com/mattn/go-sqlite3",
-)
-
-go_repository(
-    name = "com_github_sebastiaanklippert_go_wkhtmltopdf",
-    commit = "72a7793efd38728796273861bb27d590cc33d9d4",
-    importpath = "github.com/sebastiaanklippert/go-wkhtmltopdf",
-)
-
-go_repository(
-    name = "com_github_ziutek_telnet",
-    commit = "c3b780dc415b28894076b4ec975ea3ea69e3980f",
-    importpath = "github.com/ziutek/telnet",
-)
-
-go_repository(
-    name = "com_github_digitalocean_go_netbox",
-    commit = "29433ec527e78486ea0a5758817ab672d977f90e",
-    importpath = "github.com/digitalocean/go-netbox",
-)
-
-go_repository(
-    name = "com_github_cenkalti_backoff",
-    commit = "4b4cebaf850ec58f1bb1fec5bdebdf8501c2bc3f",
-    importpath = "github.com/cenkalti/backoff",
-)
-
-go_repository(
-    name = "ml_vbom_util",
-    commit = "db5cfe13f5cc",
-    importpath = "vbom.ml/util",
-)
-
-go_repository(
-    name = "com_github_go_openapi_strfmt",
-    importpath = "github.com/go-openapi/strfmt",
-    tag = "v0.19.0",
-)
-
-go_repository(
-    name = "com_github_go_openapi_swag",
-    importpath = "github.com/go-openapi/swag",
-    tag = "v0.19.5",
-)
-
-go_repository(
-    name = "com_github_go_openapi_errors",
-    importpath = "github.com/go-openapi/errors",
-    tag = "v0.19.2",
-)
-
-go_repository(
-    name = "com_github_go_openapi_runtime",
-    importpath = "github.com/go-openapi/runtime",
-    tag = "v0.19.0",
-)
-
-go_repository(
-    name = "com_github_go_openapi_validate",
-    importpath = "github.com/go-openapi/validate",
-    tag = "v0.19.2",
-)
-
-go_repository(
-    name = "com_github_mitchellh_mapstructure",
-    importpath = "github.com/mitchellh/mapstructure",
-    tag = "v1.1.2",
-)
-
-go_repository(
-    name = "org_mongodb_go_mongo_driver",
-    commit = "9ec4480161a76f5267d56fc836b7f6d357fd9209",
-    importpath = "go.mongodb.org/mongo-driver",
-)
-
-go_repository(
-    name = "in_gopkg_yaml_v2",
-    importpath = "gopkg.in/yaml.v2",
-    tag = "v2.2.4",
-)
-
-go_repository(
-    name = "com_github_asaskevich_govalidator",
-    commit = "f61b66f89f4a",
-    importpath = "github.com/asaskevich/govalidator",
-)
-
-go_repository(
-    name = "com_github_go_openapi_spec",
-    importpath = "github.com/go-openapi/spec",
-    tag = "v0.19.2",
-)
-
-go_repository(
-    name = "com_github_mailru_easyjson",
-    commit = "b2ccc519800e",
-    importpath = "github.com/mailru/easyjson",
-)
-
-go_repository(
-    name = "com_github_go_openapi_analysis",
-    importpath = "github.com/go-openapi/analysis",
-    tag = "v0.19.2",
-)
-
-go_repository(
-    name = "com_github_go_openapi_jsonpointer",
-    importpath = "github.com/go-openapi/jsonpointer",
-    tag = "v0.19.3",
-)
-
-go_repository(
-    name = "com_github_go_openapi_loads",
-    importpath = "github.com/go-openapi/loads",
-    tag = "v0.19.2",
-)
-
-go_repository(
-    name = "com_github_go_openapi_jsonreference",
-    importpath = "github.com/go-openapi/jsonreference",
-    tag = "v0.19.2",
-)
-
-go_repository(
-    name = "com_github_puerkitobio_purell",
-    importpath = "github.com/PuerkitoBio/purell",
-    tag = "v1.1.1",
-)
-
-go_repository(
-    name = "com_github_puerkitobio_urlesc",
-    commit = "de5bf2ad4578",
-    importpath = "github.com/PuerkitoBio/urlesc",
-)
-
-go_repository(
-    name = "com_github_abbot_go_http_auth",
-    commit = "860ed7f246ff5abfdbd5c7ce618fd37b49fd3d86",
-    importpath = "github.com/abbot/go-http-auth",
-)
-
-go_repository(
-    name = "com_github_urfave_cli",
-    importpath = "github.com/urfave/cli",
-    tag = "v1.20.0",
-)
-
-go_repository(
-    name = "org_golang_x_crypto",
-    commit = "60c769a6c586",
-    importpath = "golang.org/x/crypto",
-)
-
-go_repository(
-    name = "org_golang_x_oauth2",
-    commit = "0f29369cfe45",
-    importpath = "golang.org/x/oauth2",
-)
-
-go_repository(
-    name = "com_github_djherbis_atime",
-    commit = "2d569978378562c466df74eda2d82900f435c5f4",
-    importpath = "github.com/djherbis/atime",
-)
-
-go_repository(
-    name = "com_google_cloud_go",
-    importpath = "cloud.google.com/go",
-    tag = "v0.38.0",
-)
-
-go_repository(
-    name = "org_golang_x_net",
-    commit = "13f9640d40b9",
-    importpath = "golang.org/x/net",
-)
-
-go_repository(
-    name = "com_github_stackexchange_wmi",
-    commit = "cbe66965904dbe8a6cd589e2298e5d8b986bd7dd",
-    importpath = "github.com/stackexchange/wmi",
-)
-
-go_repository(
-    name = "com_github_go_ole_go_ole",
-    commit = "938323a72016e9cf84fa5fba7635089efb0ad87f",
-    importpath = "github.com/go-ole/go-ole",
-)
-
-go_repository(
-    name = "com_github_dustin_go_humanize",
-    importpath = "github.com/dustin/go-humanize",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "io_k8s_client_go",
-    commit = "0a8a1d7b7fae",
-    importpath = "k8s.io/client-go",
-)
-
-go_repository(
-    name = "io_k8s_apimachinery",
-    build_file_proto_mode = "disable",
-    commit = "731dcecc205498f52a21b12e311af095efb4b188",
-    importpath = "k8s.io/apimachinery",
-    patches = ["//third_party/go/k8s-apimachinery:fix-kubernetes-bug-87675.patch"],
-    patch_args = ["-p1"],
-)
-
-go_repository(
-    name = "io_k8s_klog",
-    importpath = "k8s.io/klog",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "io_k8s_utils",
-    commit = "e782cd3c129f",
-    importpath = "k8s.io/utils",
-)
-
-go_repository(
-    name = "com_github_googleapis_gnostic",
-    commit = "15cf44e552f9",
-    importpath = "github.com/googleapis/gnostic",
-)
-
-go_repository(
-    name = "io_k8s_api",
-    build_file_proto_mode = "disable",
-    commit = "bbc9463b57e5",
-    importpath = "k8s.io/api",
-)
-
-go_repository(
-    name = "org_golang_x_time",
-    commit = "9d24e82272b4",
-    importpath = "golang.org/x/time",
-)
-
-go_repository(
-    name = "com_github_google_gofuzz",
-    importpath = "github.com/google/gofuzz",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "io_k8s_sigs_yaml",
-    importpath = "sigs.k8s.io/yaml",
-    tag = "v1.1.0",
-)
-
-go_repository(
-    name = "com_github_modern_go_reflect2",
-    importpath = "github.com/modern-go/reflect2",
-    tag = "v1.0.1",
-)
-
-go_repository(
-    name = "com_github_davecgh_go_spew",
-    importpath = "github.com/davecgh/go-spew",
-    tag = "v1.1.1",
-)
-
-go_repository(
-    name = "com_github_json_iterator_go",
-    importpath = "github.com/json-iterator/go",
-    tag = "v1.1.8",
-)
-
-go_repository(
-    name = "com_github_modern_go_concurrent",
-    commit = "bacd9c7ef1dd",
-    importpath = "github.com/modern-go/concurrent",
-)
-
-go_repository(
-    name = "in_gopkg_inf_v0",
-    importpath = "gopkg.in/inf.v0",
-    tag = "v0.9.1",
-)
-
-go_repository(
-    name = "com_github_cloudflare_cfrpki",
-    commit = "adece784464315db69299ba75e9287c60cd95c69",
-    importpath = "github.com/cloudflare/cfrpki",
-)
-
-go_repository(
-    name = "com_github_prometheus_client_golang",
-    importpath = "github.com/prometheus/client_golang",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_rs_cors",
-    commit = "db0fe48135e83b5812a5a31be0eea66984b1b521",
-    importpath = "github.com/rs/cors",
-)
-
-go_repository(
-    name = "com_github_cloudflare_gortr",
-    commit = "95270606e8853d9b93f5be46d656d08ec0a4ef09",
-    importpath = "github.com/cloudflare/gortr",
-)
-
-go_repository(
-    name = "com_github_gorilla_mux",
-    importpath = "github.com/gorilla/mux",
-    tag = "v1.6.2",
-)
-
-go_repository(
-    name = "com_github_sirupsen_logrus",
-    importpath = "github.com/sirupsen/logrus",
-    tag = "v1.4.2",
-)
-
-go_repository(
-    name = "com_github_prometheus_common",
-    importpath = "github.com/prometheus/common",
-    tag = "v0.4.1",
-)
-
-go_repository(
-    name = "com_github_beorn7_perks",
-    importpath = "github.com/beorn7/perks",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_prometheus_client_model",
-    commit = "fd36f4220a90",
-    importpath = "github.com/prometheus/client_model",
-)
-
-go_repository(
-    name = "com_github_prometheus_procfs",
-    importpath = "github.com/prometheus/procfs",
-    tag = "v0.0.2",
-)
-
-go_repository(
-    name = "com_github_matttproud_golang_protobuf_extensions",
-    importpath = "github.com/matttproud/golang_protobuf_extensions",
-    tag = "v1.0.1",
-)
-
-go_repository(
-    name = "com_github_jmoiron_sqlx",
-    commit = "38398a30ed8516ffda617a04c822de09df8a3ec5",
-    importpath = "github.com/jmoiron/sqlx",
-)
-
-go_repository(
-    name = "com_github_lib_pq",
-    commit = "3427c32cb71afc948325f299f040e53c1dd78979",
-    importpath = "github.com/lib/pq",
-)
-
-go_repository(
-    name = "com_github_gchaincl_sqlhooks",
-    commit = "1932c8dd22f2283687586008bf2d58c2c5c014d0",
-    importpath = "github.com/gchaincl/sqlhooks",
-)
-
-go_repository(
-    name = "com_github_golang_migrate_migrate_v4",
-    commit = "e93eaeb3fe21ce2ccc1365277a01863e6bc84d9c",
-    importpath = "github.com/golang-migrate/migrate/v4",
-    remote = "https://github.com/golang-migrate/migrate",
-    vcs = "git",
-)
-
-go_repository(
-    name = "com_github_hashicorp_go_multierror",
-    commit = "bdca7bb83f603b80ef756bb953fe1dafa9cd00a2",
-    importpath = "github.com/hashicorp/go-multierror",
-)
-
-go_repository(
-    name = "com_github_hashicorp_errwrap",
-    commit = "8a6fb523712970c966eefc6b39ed2c5e74880354",
-    importpath = "github.com/hashicorp/errwrap",
-)
-
-go_repository(
-    name = "com_github_cockroachdb_cockroach_go",
-    commit = "e0a95dfd547cc9c3ebaaba1a12c2afe4bf621ac5",
-    importpath = "github.com/cockroachdb/cockroach-go",
-)
-
-go_repository(
-    name = "com_github_jackc_pgx",
-    commit = "6954c15ad0bd3c9aa6dd1b190732b020379beb28",
-    importpath = "github.com/jackc/pgx",
-)
-
-go_repository(
-    name = "com_github_golang_collections_go_datastructures",
-    commit = "59788d5eb2591d3497ffb8fafed2f16fe00e7775",
-    importpath = "github.com/golang-collections/go-datastructures",
-)
-
-go_repository(
-    name = "com_github_go_test_deep",
-    commit = "cf67d735e69b4a4d50cdf571a92b0144786080f7",
-    importpath = "github.com/go-test/deep",
-)
-
-go_repository(
-    name = "com_github_sethvargo_go_password",
-    commit = "68ac5879751a7105834296859f8c1bf70b064675",
-    importpath = "github.com/sethvargo/go-password",
-)
-
-go_repository(
-    name = "in_gopkg_ldap_v3",
-    commit = "9f0d712775a0973b7824a1585a86a4ea1d5263d9",
-    importpath = "gopkg.in/ldap.v3",
-)
-
-go_repository(
-    name = "in_gopkg_asn1_ber_v1",
-    commit = "f715ec2f112d1e4195b827ad68cf44017a3ef2b1",
-    importpath = "gopkg.in/asn1-ber.v1",
-)
-
-go_repository(
-    name = "com_github_q3k_cursedjsonrpc",
-    commit = "304f0561c9162a2696f3ae7c96f3404324177ab8",
-    importpath = "github.com/q3k/cursedjsonrpc",
-)
-
-go_repository(
-    name = "com_github_q3k_cursedjson",
-    commit = "af0e3abb1bcef7197b3b9f91d7d094e6528a2d05",
-    importpath = "github.com/q3k/cursedjson",
-)
-
-go_repository(
-    name = "co_honnef_go_tools",
-    commit = "ea95bdfd59fc",
-    importpath = "honnef.co/go/tools",
-)
-
-go_repository(
-    name = "com_github_azure_go_ansiterm",
-    commit = "d6e3b3328b78",
-    importpath = "github.com/Azure/go-ansiterm",
-)
-
-go_repository(
-    name = "com_github_azure_go_autorest",
-    importpath = "github.com/Azure/go-autorest",
-    tag = "v11.5.0",
-)
-
-local_repository(
-    name = "com_github_census_instrumentation_opencensus_proto",
-    path = "./third_party/go/opencensus-proto",
-)
-
-go_repository(
-    name = "com_github_client9_misspell",
-    importpath = "github.com/client9/misspell",
-    tag = "v0.3.4",
-)
-
-go_repository(
-    name = "com_github_containerd_continuity",
-    commit = "7f53d412b9eb",
-    importpath = "github.com/containerd/continuity",
-)
-
-go_repository(
-    name = "com_github_coreos_clair",
-    commit = "44ae4bc9590a",
-    importpath = "github.com/coreos/clair",
-)
-
-go_repository(
-    name = "com_github_dgrijalva_jwt_go",
-    importpath = "github.com/dgrijalva/jwt-go",
-    tag = "v3.2.0",
-)
-
-go_repository(
-    name = "com_github_docker_cli",
-    commit = "54c19e67f69c",
-    importpath = "github.com/docker/cli",
-)
-
-go_repository(
-    name = "com_github_docker_distribution",
-    importpath = "github.com/docker/distribution",
-    tag = "v2.7.1",
-)
-
-go_repository(
-    name = "com_github_docker_docker",
-    commit = "be7ac8be2ae0",
-    importpath = "github.com/docker/docker",
-)
-
-go_repository(
-    name = "com_github_docker_docker_ce",
-    commit = "f53bd8bb8e43",
-    importpath = "github.com/docker/docker-ce",
-)
-
-go_repository(
-    name = "com_github_docker_docker_credential_helpers",
-    importpath = "github.com/docker/docker-credential-helpers",
-    tag = "v0.6.1",
-)
-
-go_repository(
-    name = "com_github_docker_go_connections",
-    commit = "97c2040d34df",
-    importpath = "github.com/docker/go-connections",
-)
-
-go_repository(
-    name = "com_github_docker_go_metrics",
-    commit = "399ea8c73916",
-    importpath = "github.com/docker/go-metrics",
-)
-
-go_repository(
-    name = "com_github_docker_go_units",
-    importpath = "github.com/docker/go-units",
-    tag = "v0.3.3",
-)
-
-go_repository(
-    name = "com_github_docker_libtrust",
-    commit = "aabc10ec26b7",
-    importpath = "github.com/docker/libtrust",
-)
-
-go_repository(
-    name = "com_github_elazarl_go_bindata_assetfs",
-    commit = "38087fe4dafb",
-    importpath = "github.com/elazarl/go-bindata-assetfs",
-)
-
-go_repository(
-    name = "com_github_evanphx_json_patch",
-    importpath = "github.com/evanphx/json-patch",
-    tag = "v4.2.0",
-)
-
-go_repository(
-    name = "com_github_fernet_fernet_go",
-    commit = "9eac43b88a5e",
-    importpath = "github.com/fernet/fernet-go",
-)
-
-go_repository(
-    name = "com_github_fsnotify_fsnotify",
-    importpath = "github.com/fsnotify/fsnotify",
-    tag = "v1.4.7",
-)
-
-go_repository(
-    name = "com_github_genuinetools_pkg",
-    commit = "1c141f661797",
-    importpath = "github.com/genuinetools/pkg",
-)
-
-go_repository(
-    name = "com_github_genuinetools_reg",
-    commit = "d959057b30da",
-    importpath = "github.com/genuinetools/reg",
-)
-
-go_repository(
-    name = "com_github_ghodss_yaml",
-    importpath = "github.com/ghodss/yaml",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_gogo_protobuf",
-    commit = "65acae22fc9d",
-    importpath = "github.com/gogo/protobuf",
-)
-
-go_repository(
-    name = "com_github_golang_glog",
-    commit = "23def4e6c14b",
-    importpath = "github.com/golang/glog",
-)
-
-go_repository(
-    name = "com_github_golang_mock",
-    importpath = "github.com/golang/mock",
-    tag = "v1.2.0",
-)
-
-go_repository(
-    name = "com_github_golang_protobuf",
-    importpath = "github.com/golang/protobuf",
-    tag = "v1.3.2",
-)
-
-go_repository(
-    name = "com_github_google_btree",
-    importpath = "github.com/google/btree",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_google_go_cmp",
-    importpath = "github.com/google/go-cmp",
-    tag = "v0.3.0",
-)
-
-go_repository(
-    name = "com_github_google_go_jsonnet",
-    importpath = "github.com/google/go-jsonnet",
-    tag = "v0.12.1",
-)
-
-go_repository(
-    name = "com_github_gophercloud_gophercloud",
-    importpath = "github.com/gophercloud/gophercloud",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_gorilla_context",
-    importpath = "github.com/gorilla/context",
-    tag = "v1.1.1",
-)
-
-go_repository(
-    name = "com_github_gregjones_httpcache",
-    commit = "9cad4c3443a7",
-    importpath = "github.com/gregjones/httpcache",
-)
-
-go_repository(
-    name = "com_github_grpc_ecosystem_grpc_gateway",
-    importpath = "github.com/grpc-ecosystem/grpc-gateway",
-    tag = "v1.9.5",
-)
-
-go_repository(
-    name = "com_github_hpcloud_tail",
-    importpath = "github.com/hpcloud/tail",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_imdario_mergo",
-    importpath = "github.com/imdario/mergo",
-    tag = "v0.3.5",
-)
-
-go_repository(
-    name = "com_github_inconshreveable_mousetrap",
-    importpath = "github.com/inconshreveable/mousetrap",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_kisielk_gotool",
-    importpath = "github.com/kisielk/gotool",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_kr_pretty",
-    importpath = "github.com/kr/pretty",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_kr_pty",
-    importpath = "github.com/kr/pty",
-    tag = "v1.1.5",
-)
-
-go_repository(
-    name = "com_github_kr_text",
-    importpath = "github.com/kr/text",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_mattn_go_isatty",
-    importpath = "github.com/mattn/go-isatty",
-    tag = "v0.0.4",
-)
-
-go_repository(
-    name = "com_github_microsoft_go_winio",
-    importpath = "github.com/Microsoft/go-winio",
-    tag = "v0.4.11",
-)
-
-go_repository(
-    name = "com_github_mitchellh_go_wordwrap",
-    importpath = "github.com/mitchellh/go-wordwrap",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_nvveen_gotty",
-    commit = "cd527374f1e5",
-    importpath = "github.com/Nvveen/Gotty",
-)
-
-go_repository(
-    name = "com_github_onsi_ginkgo",
-    importpath = "github.com/onsi/ginkgo",
-    tag = "v1.10.1",
-)
-
-go_repository(
-    name = "com_github_onsi_gomega",
-    importpath = "github.com/onsi/gomega",
-    tag = "v1.7.0",
-)
-
-go_repository(
-    name = "com_github_opencontainers_go_digest",
-    importpath = "github.com/opencontainers/go-digest",
-    tag = "v1.0.0-rc1",
-)
-
-go_repository(
-    name = "com_github_opencontainers_image_spec",
-    importpath = "github.com/opencontainers/image-spec",
-    tag = "v1.0.1",
-)
-
-go_repository(
-    name = "com_github_opencontainers_runc",
-    importpath = "github.com/opencontainers/runc",
-    tag = "v0.1.1",
-)
-
-go_repository(
-    name = "com_github_openzipkin_zipkin_go",
-    importpath = "github.com/openzipkin/zipkin-go",
-    tag = "v0.1.3",
-)
-
-go_repository(
-    name = "com_github_peterbourgon_diskv",
-    importpath = "github.com/peterbourgon/diskv",
-    tag = "v2.0.1",
-)
-
-go_repository(
-    name = "com_github_peterhellberg_link",
-    importpath = "github.com/peterhellberg/link",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_pkg_errors",
-    importpath = "github.com/pkg/errors",
-    tag = "v0.8.1",
-)
-
-go_repository(
-    name = "com_github_pmezard_go_difflib",
-    importpath = "github.com/pmezard/go-difflib",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_sergi_go_diff",
-    commit = "feef008d51ad",
-    importpath = "github.com/sergi/go-diff",
-)
-
-go_repository(
-    name = "com_github_shurcool_httpfs",
-    commit = "809beceb2371",
-    importpath = "github.com/shurcooL/httpfs",
-)
-
-go_repository(
-    name = "com_github_spf13_cobra",
-    importpath = "github.com/spf13/cobra",
-    tag = "v0.0.5",
-)
-
-go_repository(
-    name = "com_github_spf13_pflag",
-    importpath = "github.com/spf13/pflag",
-    tag = "v1.0.5",
-)
-
-go_repository(
-    name = "com_github_stretchr_objx",
-    importpath = "github.com/stretchr/objx",
-    tag = "v0.2.0",
-)
-
-go_repository(
-    name = "com_github_stretchr_testify",
-    importpath = "github.com/stretchr/testify",
-    tag = "v1.3.0",
-)
-
-go_repository(
-    name = "in_gopkg_airbrake_gobrake_v2",
-    importpath = "gopkg.in/airbrake/gobrake.v2",
-    tag = "v2.0.9",
-)
-
-go_repository(
-    name = "in_gopkg_check_v1",
-    commit = "788fd7840127",
-    importpath = "gopkg.in/check.v1",
-)
-
-go_repository(
-    name = "in_gopkg_fsnotify_v1",
-    importpath = "gopkg.in/fsnotify.v1",
-    tag = "v1.4.7",
-)
-
-go_repository(
-    name = "in_gopkg_gemnasium_logrus_airbrake_hook_v2",
-    importpath = "gopkg.in/gemnasium/logrus-airbrake-hook.v2",
-    tag = "v2.1.2",
-)
-
-go_repository(
-    name = "in_gopkg_tomb_v1",
-    commit = "dd632973f1e7",
-    importpath = "gopkg.in/tomb.v1",
-)
-
-go_repository(
-    name = "io_k8s_apiextensions_apiserver",
-    build_file_proto_mode = "disable",
-    commit = "b615a37f53e7",
-    importpath = "k8s.io/apiextensions-apiserver",
-)
-
-go_repository(
-    name = "io_k8s_kube_openapi",
-    commit = "30be4d16710a",
-    importpath = "k8s.io/kube-openapi",
-)
-
-go_repository(
-    name = "io_opencensus_go",
-    importpath = "go.opencensus.io",
-    tag = "v0.21.0",
-)
-
-go_repository(
-    name = "io_opencensus_go_contrib_exporter_ocagent",
-    importpath = "contrib.go.opencensus.io/exporter/ocagent",
-    tag = "v0.6.0",
-)
-
-go_repository(
-    name = "org_apache_git_thrift_git",
-    commit = "9b75e4fe745a",
-    importpath = "git.apache.org/thrift.git",
-)
-
-go_repository(
-    name = "org_golang_google_api",
-    importpath = "google.golang.org/api",
-    tag = "v0.4.0",
-)
-
-go_repository(
-    name = "org_golang_google_appengine",
-    importpath = "google.golang.org/appengine",
-    tag = "v1.5.0",
-)
-
-go_repository(
-    name = "org_golang_google_genproto",
-    commit = "54afdca5d873",
-    importpath = "google.golang.org/genproto",
-)
-
-go_repository(
-    name = "org_golang_google_grpc",
-    importpath = "google.golang.org/grpc",
-    tag = "v1.23.1",
-)
-
-go_repository(
-    name = "org_golang_x_lint",
-    commit = "d0100b6bd8b3",
-    importpath = "golang.org/x/lint",
-)
-
-go_repository(
-    name = "org_golang_x_sync",
-    commit = "112230192c58",
-    importpath = "golang.org/x/sync",
-)
-
-go_repository(
-    name = "org_golang_x_sys",
-    commit = "c7b8b68b1456",
-    importpath = "golang.org/x/sys",
-)
-
-go_repository(
-    name = "org_golang_x_text",
-    importpath = "golang.org/x/text",
-    tag = "v0.3.2",
-)
-
-go_repository(
-    name = "org_golang_x_tools",
-    commit = "5eefd052ad72",
-    importpath = "golang.org/x/tools",
-)
-
-go_repository(
-    name = "tools_gotest",
-    importpath = "gotest.tools",
-    tag = "v2.2.0",
-)
-
-go_repository(
-    name = "com_github_alecthomas_template",
-    commit = "a0175ee3bccc",
-    importpath = "github.com/alecthomas/template",
-)
-
-go_repository(
-    name = "com_github_alecthomas_units",
-    commit = "2efee857e7cf",
-    importpath = "github.com/alecthomas/units",
-)
-
-go_repository(
-    name = "com_github_armon_consul_api",
-    commit = "eb2c6b5be1b6",
-    importpath = "github.com/armon/consul-api",
-)
-
-go_repository(
-    name = "com_github_azure_go_autorest_autorest",
-    importpath = "github.com/Azure/go-autorest/autorest",
-    tag = "v0.9.0",
-)
-
-go_repository(
-    name = "com_github_azure_go_autorest_autorest_adal",
-    importpath = "github.com/Azure/go-autorest/autorest/adal",
-    tag = "v0.5.0",
-)
-
-go_repository(
-    name = "com_github_azure_go_autorest_autorest_date",
-    importpath = "github.com/Azure/go-autorest/autorest/date",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_azure_go_autorest_autorest_mocks",
-    importpath = "github.com/Azure/go-autorest/autorest/mocks",
-    tag = "v0.2.0",
-)
-
-go_repository(
-    name = "com_github_azure_go_autorest_logger",
-    importpath = "github.com/Azure/go-autorest/logger",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_azure_go_autorest_tracing",
-    importpath = "github.com/Azure/go-autorest/tracing",
-    tag = "v0.5.0",
-)
-
-go_repository(
-    name = "com_github_bgentry_speakeasy",
-    importpath = "github.com/bgentry/speakeasy",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_blang_semver",
-    importpath = "github.com/blang/semver",
-    tag = "v3.5.0",
-)
-
-go_repository(
-    name = "com_github_burntsushi_toml",
-    importpath = "github.com/BurntSushi/toml",
-    tag = "v0.3.1",
-)
-
-go_repository(
-    name = "com_github_burntsushi_xgb",
-    commit = "27f122750802",
-    importpath = "github.com/BurntSushi/xgb",
-)
-
-go_repository(
-    name = "com_github_chai2010_gettext_go",
-    commit = "c6fed771bfd5",
-    importpath = "github.com/chai2010/gettext-go",
-)
-
-go_repository(
-    name = "com_github_cockroachdb_datadriven",
-    commit = "80d97fb3cbaa",
-    importpath = "github.com/cockroachdb/datadriven",
-)
-
-go_repository(
-    name = "com_github_coreos_etcd",
-    importpath = "github.com/coreos/etcd",
-    tag = "v3.3.10",
-)
-
-go_repository(
-    name = "com_github_coreos_go_etcd",
-    importpath = "github.com/coreos/go-etcd",
-    tag = "v2.0.0",
-)
-
-go_repository(
-    name = "com_github_coreos_go_oidc",
-    importpath = "github.com/coreos/go-oidc",
-    tag = "v2.1.0",
-)
-
-go_repository(
-    name = "com_github_coreos_go_semver",
-    importpath = "github.com/coreos/go-semver",
-    tag = "v0.3.0",
-)
-
-go_repository(
-    name = "com_github_coreos_go_systemd",
-    commit = "95778dfbb74e",
-    importpath = "github.com/coreos/go-systemd",
-)
-
-go_repository(
-    name = "com_github_coreos_pkg",
-    commit = "97fdf19511ea",
-    importpath = "github.com/coreos/pkg",
-)
-
-go_repository(
-    name = "com_github_cpuguy83_go_md2man",
-    importpath = "github.com/cpuguy83/go-md2man",
-    tag = "v1.0.10",
-)
-
-go_repository(
-    name = "com_github_creack_pty",
-    importpath = "github.com/creack/pty",
-    tag = "v1.1.7",
-)
-
-go_repository(
-    name = "com_github_daviddengcn_go_colortext",
-    commit = "511bcaf42ccd",
-    importpath = "github.com/daviddengcn/go-colortext",
-)
-
-go_repository(
-    name = "com_github_docker_spdystream",
-    commit = "449fdfce4d96",
-    importpath = "github.com/docker/spdystream",
-)
-
-go_repository(
-    name = "com_github_elazarl_goproxy",
-    commit = "c4fc26588b6e",
-    importpath = "github.com/elazarl/goproxy",
-)
-
-go_repository(
-    name = "com_github_emicklei_go_restful",
-    importpath = "github.com/emicklei/go-restful",
-    tag = "v2.9.5",
-)
-
-go_repository(
-    name = "com_github_exponent_io_jsonpath",
-    commit = "d6023ce2651d",
-    importpath = "github.com/exponent-io/jsonpath",
-)
-
-go_repository(
-    name = "com_github_fatih_camelcase",
-    importpath = "github.com/fatih/camelcase",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_fatih_color",
-    importpath = "github.com/fatih/color",
-    tag = "v1.7.0",
-)
-
-go_repository(
-    name = "com_github_globalsign_mgo",
-    commit = "eeefdecb41b8",
-    importpath = "github.com/globalsign/mgo",
-)
-
-go_repository(
-    name = "com_github_go_kit_kit",
-    importpath = "github.com/go-kit/kit",
-    tag = "v0.8.0",
-)
-
-go_repository(
-    name = "com_github_go_logfmt_logfmt",
-    importpath = "github.com/go-logfmt/logfmt",
-    tag = "v0.3.0",
-)
-
-go_repository(
-    name = "com_github_go_logr_logr",
-    importpath = "github.com/go-logr/logr",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_go_stack_stack",
-    importpath = "github.com/go-stack/stack",
-    tag = "v1.8.0",
-)
-
-go_repository(
-    name = "com_github_golang_groupcache",
-    commit = "02826c3e7903",
-    importpath = "github.com/golang/groupcache",
-)
-
-go_repository(
-    name = "com_github_golangplus_bytes",
-    commit = "45c989fe5450",
-    importpath = "github.com/golangplus/bytes",
-)
-
-go_repository(
-    name = "com_github_golangplus_fmt",
-    commit = "2a5d6d7d2995",
-    importpath = "github.com/golangplus/fmt",
-)
-
-go_repository(
-    name = "com_github_golangplus_testing",
-    commit = "af21d9c3145e",
-    importpath = "github.com/golangplus/testing",
-)
-
-go_repository(
-    name = "com_github_google_martian",
-    importpath = "github.com/google/martian",
-    tag = "v2.1.0",
-)
-
-go_repository(
-    name = "com_github_google_pprof",
-    commit = "3ea8567a2e57",
-    importpath = "github.com/google/pprof",
-)
-
-go_repository(
-    name = "com_github_google_uuid",
-    importpath = "github.com/google/uuid",
-    tag = "v1.1.1",
-)
-
-go_repository(
-    name = "com_github_googleapis_gax_go_v2",
-    importpath = "github.com/googleapis/gax-go/v2",
-    tag = "v2.0.4",
-)
-
-go_repository(
-    name = "com_github_gorilla_websocket",
-    importpath = "github.com/gorilla/websocket",
-    tag = "v1.4.0",
-)
-
-go_repository(
-    name = "com_github_grpc_ecosystem_go_grpc_middleware",
-    commit = "f849b5445de4",
-    importpath = "github.com/grpc-ecosystem/go-grpc-middleware",
-)
-
-go_repository(
-    name = "com_github_grpc_ecosystem_go_grpc_prometheus",
-    importpath = "github.com/grpc-ecosystem/go-grpc-prometheus",
-    tag = "v1.2.0",
-)
-
-go_repository(
-    name = "com_github_hashicorp_golang_lru",
-    importpath = "github.com/hashicorp/golang-lru",
-    tag = "v0.5.1",
-)
-
-go_repository(
-    name = "com_github_hashicorp_hcl",
-    importpath = "github.com/hashicorp/hcl",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_jonboulle_clockwork",
-    importpath = "github.com/jonboulle/clockwork",
-    tag = "v0.1.0",
-)
-
-go_repository(
-    name = "com_github_jstemmer_go_junit_report",
-    commit = "af01ea7f8024",
-    importpath = "github.com/jstemmer/go-junit-report",
-)
-
-go_repository(
-    name = "com_github_julienschmidt_httprouter",
-    importpath = "github.com/julienschmidt/httprouter",
-    tag = "v1.2.0",
-)
-
-go_repository(
-    name = "com_github_kisielk_errcheck",
-    importpath = "github.com/kisielk/errcheck",
-    tag = "v1.2.0",
-)
-
-go_repository(
-    name = "com_github_konsorten_go_windows_terminal_sequences",
-    importpath = "github.com/konsorten/go-windows-terminal-sequences",
-    tag = "v1.0.1",
-)
-
-go_repository(
-    name = "com_github_kr_logfmt",
-    commit = "b84e30acd515",
-    importpath = "github.com/kr/logfmt",
-)
-
-go_repository(
-    name = "com_github_liggitt_tabwriter",
-    commit = "89fcab3d43de",
-    importpath = "github.com/liggitt/tabwriter",
-)
-
-go_repository(
-    name = "com_github_lithammer_dedent",
-    importpath = "github.com/lithammer/dedent",
-    tag = "v1.1.0",
-)
-
-go_repository(
-    name = "com_github_magiconair_properties",
-    importpath = "github.com/magiconair/properties",
-    tag = "v1.8.0",
-)
-
-go_repository(
-    name = "com_github_makenowjust_heredoc",
-    commit = "bb23615498cd",
-    importpath = "github.com/MakeNowJust/heredoc",
-)
-
-go_repository(
-    name = "com_github_mattn_go_colorable",
-    importpath = "github.com/mattn/go-colorable",
-    tag = "v0.0.9",
-)
-
-go_repository(
-    name = "com_github_mattn_go_runewidth",
-    importpath = "github.com/mattn/go-runewidth",
-    tag = "v0.0.2",
-)
-
-go_repository(
-    name = "com_github_mitchellh_go_homedir",
-    importpath = "github.com/mitchellh/go-homedir",
-    tag = "v1.1.0",
-)
-
-go_repository(
-    name = "com_github_morikuni_aec",
-    importpath = "github.com/morikuni/aec",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_munnerz_goautoneg",
-    commit = "a7dc8b61c822",
-    importpath = "github.com/munnerz/goautoneg",
-)
-
-go_repository(
-    name = "com_github_mwitkow_go_conntrack",
-    commit = "cc309e4a2223",
-    importpath = "github.com/mwitkow/go-conntrack",
-)
-
-go_repository(
-    name = "com_github_mxk_go_flowrate",
-    commit = "cca7078d478f",
-    importpath = "github.com/mxk/go-flowrate",
-)
-
-go_repository(
-    name = "com_github_nytimes_gziphandler",
-    commit = "56545f4a5d46",
-    importpath = "github.com/NYTimes/gziphandler",
-)
-
-go_repository(
-    name = "com_github_olekukonko_tablewriter",
-    commit = "a0225b3f23b5",
-    importpath = "github.com/olekukonko/tablewriter",
-)
-
-go_repository(
-    name = "com_github_pelletier_go_toml",
-    importpath = "github.com/pelletier/go-toml",
-    tag = "v1.2.0",
-)
-
-go_repository(
-    name = "com_github_pquerna_cachecontrol",
-    commit = "0dec1b30a021",
-    importpath = "github.com/pquerna/cachecontrol",
-)
-
-go_repository(
-    name = "com_github_remyoudompheng_bigfft",
-    commit = "52369c62f446",
-    importpath = "github.com/remyoudompheng/bigfft",
-)
-
-go_repository(
-    name = "com_github_rogpeppe_fastuuid",
-    commit = "6724a57986af",
-    importpath = "github.com/rogpeppe/fastuuid",
-)
-
-go_repository(
-    name = "com_github_russross_blackfriday",
-    importpath = "github.com/russross/blackfriday",
-    tag = "v1.5.2",
-)
-
-go_repository(
-    name = "com_github_soheilhy_cmux",
-    importpath = "github.com/soheilhy/cmux",
-    tag = "v0.1.4",
-)
-
-go_repository(
-    name = "com_github_spf13_afero",
-    importpath = "github.com/spf13/afero",
-    tag = "v1.2.2",
-)
-
-go_repository(
-    name = "com_github_spf13_cast",
-    importpath = "github.com/spf13/cast",
-    tag = "v1.3.0",
-)
-
-go_repository(
-    name = "com_github_spf13_jwalterweatherman",
-    importpath = "github.com/spf13/jwalterweatherman",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "com_github_spf13_viper",
-    importpath = "github.com/spf13/viper",
-    tag = "v1.3.2",
-)
-
-go_repository(
-    name = "com_github_tmc_grpc_websocket_proxy",
-    commit = "89b8d40f7ca8",
-    importpath = "github.com/tmc/grpc-websocket-proxy",
-)
-
-go_repository(
-    name = "com_github_ugorji_go_codec",
-    commit = "d75b2dcb6bc8",
-    importpath = "github.com/ugorji/go/codec",
-)
-
-go_repository(
-    name = "com_github_xiang90_probing",
-    commit = "43a291ad63a2",
-    importpath = "github.com/xiang90/probing",
-)
-
-go_repository(
-    name = "com_github_xlab_handysort",
-    commit = "fb3537ed64a1",
-    importpath = "github.com/xlab/handysort",
-)
-
-go_repository(
-    name = "com_github_xordataexchange_crypt",
-    commit = "b2862e3d0a77",
-    importpath = "github.com/xordataexchange/crypt",
-)
-
-go_repository(
-    name = "in_gopkg_alecthomas_kingpin_v2",
-    importpath = "gopkg.in/alecthomas/kingpin.v2",
-    tag = "v2.2.6",
-)
-
-go_repository(
-    name = "in_gopkg_cheggaaa_pb_v1",
-    importpath = "gopkg.in/cheggaaa/pb.v1",
-    tag = "v1.0.25",
-)
-
-go_repository(
-    name = "in_gopkg_natefinch_lumberjack_v2",
-    importpath = "gopkg.in/natefinch/lumberjack.v2",
-    tag = "v2.0.0",
-)
-
-go_repository(
-    name = "in_gopkg_resty_v1",
-    importpath = "gopkg.in/resty.v1",
-    tag = "v1.12.0",
-)
-
-go_repository(
-    name = "in_gopkg_square_go_jose_v2",
-    importpath = "gopkg.in/square/go-jose.v2",
-    tag = "v2.2.2",
-)
-
-go_repository(
-    name = "io_etcd_go_bbolt",
-    importpath = "go.etcd.io/bbolt",
-    tag = "v1.3.3",
-)
-
-go_repository(
-    name = "io_etcd_go_etcd",
-    commit = "3cf2f69b5738",
-    importpath = "go.etcd.io/etcd",
-)
-
-go_repository(
-    name = "io_k8s_apiserver",
-    commit = "f2537b84c964",
-    importpath = "k8s.io/apiserver",
-)
-
-go_repository(
-    name = "io_k8s_cli_runtime",
-    commit = "ec04ad4dbd24",
-    importpath = "k8s.io/cli-runtime",
-)
-
-go_repository(
-    name = "io_k8s_code_generator",
-    commit = "2a85f169f05f",
-    importpath = "k8s.io/code-generator",
-)
-
-go_repository(
-    name = "io_k8s_component_base",
-    commit = "ea09a2678486",
-    importpath = "k8s.io/component-base",
-)
-
-go_repository(
-    name = "io_k8s_gengo",
-    commit = "26a664648505",
-    importpath = "k8s.io/gengo",
-)
-
-go_repository(
-    name = "io_k8s_kubectl",
-    commit = "fbc5d36fee2d",
-    importpath = "k8s.io/kubectl",
-)
-
-go_repository(
-    name = "io_k8s_metrics",
-    commit = "dea8d0e6b550",
-    importpath = "k8s.io/metrics",
-)
-
-go_repository(
-    name = "io_k8s_sigs_kustomize",
-    importpath = "sigs.k8s.io/kustomize",
-    tag = "v2.0.3",
-)
-
-go_repository(
-    name = "io_k8s_sigs_structured_merge_diff",
-    commit = "b1b620dd3f06",
-    importpath = "sigs.k8s.io/structured-merge-diff",
-)
-
-go_repository(
-    name = "org_golang_x_exp",
-    commit = "4b39c73a6495",
-    importpath = "golang.org/x/exp",
-)
-
-go_repository(
-    name = "org_golang_x_image",
-    commit = "0694c2d4d067",
-    importpath = "golang.org/x/image",
-)
-
-go_repository(
-    name = "org_golang_x_mobile",
-    commit = "d3739f865fa6",
-    importpath = "golang.org/x/mobile",
-)
-
-go_repository(
-    name = "org_golang_x_xerrors",
-    commit = "a985d3407aa7",
-    importpath = "golang.org/x/xerrors",
-)
-
-go_repository(
-    name = "org_gonum_v1_gonum",
-    commit = "3d26580ed485",
-    importpath = "gonum.org/v1/gonum",
-)
-
-go_repository(
-    name = "org_gonum_v1_netlib",
-    commit = "76723241ea4e",
-    importpath = "gonum.org/v1/netlib",
-)
-
-go_repository(
-    name = "org_modernc_cc",
-    importpath = "modernc.org/cc",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "org_modernc_golex",
-    importpath = "modernc.org/golex",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "org_modernc_mathutil",
-    importpath = "modernc.org/mathutil",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "org_modernc_strutil",
-    importpath = "modernc.org/strutil",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "org_modernc_xc",
-    importpath = "modernc.org/xc",
-    tag = "v1.0.0",
-)
-
-go_repository(
-    name = "org_uber_go_atomic",
-    importpath = "go.uber.org/atomic",
-    tag = "v1.3.2",
-)
-
-go_repository(
-    name = "org_uber_go_multierr",
-    importpath = "go.uber.org/multierr",
-    tag = "v1.1.0",
-)
-
-go_repository(
-    name = "org_uber_go_zap",
-    importpath = "go.uber.org/zap",
-    tag = "v1.10.0",
-)
-
-go_repository(
-    name = "com_github_dgraph_io_ristretto",
-    commit = "83508260cb49a2c3261c2774c991870fd18b5a1b",
-    importpath = "github.com/dgraph-io/ristretto",
-)
-
-go_repository(
-    name = "com_github_cespare_xxhash",
-    commit = "d7df74196a9e781ede915320c11c378c1b2f3a1f",
-    importpath = "github.com/cespare/xxhash",
-)
-
-go_repository(
-    name = "com_github_ulule_limiter_v3",
-    commit = "6911899e37a5788df86f770b3f85c1c3eb0313d5",
-    importpath = "github.com/ulule/limiter/v3",
-    remote = "https://github.com/ulule/limiter",
-    vcs = "git",
-)
-
-go_repository(
-    name = "com_github_go_telegram_bot_api_telegram_bot_api",
-    commit = "b33efeebc78563cfeddf19563781cffb16aaabdf",
-    importpath = "github.com/go-telegram-bot-api/telegram-bot-api",
-)
-
-go_repository(
-    name = "com_github_technoweenie_multipartstreamer",
-    commit = "a90a01d73ae432e2611d178c18367fbaa13e0154",
-    importpath = "github.com/technoweenie/multipartstreamer",
-)
-
-go_repository(
-    name = "in_gopkg_irc_v3",
-    commit = "d07dcb9293789fdc99c797d3499a5799bc343b86",
-    importpath = "gopkg.in/irc.v3",
+http_archive(
+    name = "com_github_stedolan_jq",
+    urls = ["https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gz"],
+    strip_prefix = "jq-1.6",
+    sha256 = "5de8c8e29aaa3fb9cc6b47bb27299f271354ebb72514e3accadc7d38b5bbaa72",
+    build_file = "@hscloud//third_party/jq:BUILD.external",
 )
diff --git a/app/matrix/OWNERS b/app/matrix/OWNERS
new file mode 100644
index 0000000..789b750
--- /dev/null
+++ b/app/matrix/OWNERS
@@ -0,0 +1,4 @@
+inherited: false
+owners:
+  - informatic
+  - q3k
diff --git a/app/matrix/appservice-irc.libsonnet b/app/matrix/appservice-irc.libsonnet
new file mode 100644
index 0000000..51e4f2c
--- /dev/null
+++ b/app/matrix/appservice-irc.libsonnet
@@ -0,0 +1,96 @@
+local kube = import "../../kube/kube.libsonnet";
+
+{
+    AppServiceIrc(name):: {
+        local bridge = self,
+        local cfg = bridge.cfg,
+        cfg:: {
+            metadata: {},
+            config: std.native("parseYaml")(importstr "appservice-irc.yaml")[0],
+            image: error "image must be set",
+            storageClassName: error "storageClassName must be set",
+        },
+
+        config: kube.ConfigMap("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            data: {
+                "config.yaml": std.manifestJsonEx(cfg.config, ""),
+            },
+        },
+
+        dataVolume: kube.PersistentVolumeClaim("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            spec+: {
+                storageClassName: cfg.storageClassName,
+                accessModes: [ "ReadWriteOnce" ],
+                resources: {
+                    requests: {
+                        storage: "10Gi",
+                    },
+                },
+            },
+        },
+
+        bootstrapJob: kube.Job("appservice-irc-%s-bootstrap" % [name]) {
+            metadata+: cfg.metadata {
+                labels: {
+                    "job-name": "appservice-irc-%s-bootstrap" % [name],
+                },
+            },
+            spec+: {
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.ConfigMapVolume(bridge.config),
+                        },
+                        containers_: {
+                            bootstrap: kube.Container("appservice-irc-%s-bootstrap" % [name]) {
+                                image: cfg.image,
+                                command: ["sh", "-c", "node app.js -r -u http://appservice-irc-%s:9999 -c /config/config.yaml -f /tmp/registration.yaml && cat /tmp/registration.yaml" % [name]],
+                                volumeMounts_: {
+                                    config: { mountPath: "/config" },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+
+        deployment: kube.Deployment("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            spec+: {
+                replicas: 1,
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.ConfigMapVolume(bridge.config),
+                            data: kube.PersistentVolumeClaimVolume(bridge.dataVolume),
+                            registration: { secret: { secretName: "appservice-irc-%s-registration" % [name] } },
+                        },
+                        nodeSelector: cfg.nodeSelector,
+                        containers_: {
+                            appserviceIrc: kube.Container("appservice-irc-%s" % [name]) {
+                                image: cfg.image,
+                                command: ["node", "app.js", "-c", "/config/config.yaml", "-f", "/registration/registration.yaml", "-p", "9999"],
+                                ports_: {
+                                    http: { containerPort: 9999 },
+                                },
+                                volumeMounts_: {
+                                    registration: { mountPath: "/registration", },
+                                    config: { mountPath: "/config", },
+                                    data: { mountPath: "/data" },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+
+        svc: kube.Service("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            target_pod:: bridge.deployment.spec.template,
+        },
+    },
+}
diff --git a/app/matrix/appservice-irc.yaml b/app/matrix/appservice-irc.yaml
index b49d11c..cc65926 100644
--- a/app/matrix/appservice-irc.yaml
+++ b/app/matrix/appservice-irc.yaml
@@ -381,11 +381,6 @@
       - "1d"
       - "1w"
 
-  # The nedb database URI to connect to. This is the name of the directory to
-  # dump .db files to. This is relative to the project directory.
-  # Required.
-  databaseUri: "nedb://data"
-
   # Configuration options for the debug HTTP API. To access this API, you must
   # append ?access_token=$APPSERVICE_TOKEN (from the registration file) to the requests.
   #
@@ -443,3 +438,7 @@
   # accidentally overloading the homeserver. Defaults to 1000, which should be
   # enough for the vast majority of use cases.
   maxHttpSockets: 1000
+
+database:
+  engine: "nedb"
+  connectionString: "nedb:///data"
diff --git a/app/matrix/appservice-telegram.libsonnet b/app/matrix/appservice-telegram.libsonnet
new file mode 100644
index 0000000..b174225
--- /dev/null
+++ b/app/matrix/appservice-telegram.libsonnet
@@ -0,0 +1,157 @@
+local kube = import "../../kube/kube.libsonnet";
+
+{
+    AppServiceTelegram(name):: {
+        local bridge = self,
+        local cfg = bridge.cfg,
+        cfg:: {
+            metadata: {},
+            image: error "image must be set",
+            storageClassName: error "storageClassName must be set",
+
+            // Data that will be serialized into the appservice's config.yaml.
+            // This is taken straight from a YAML that was generated by
+            // dock.mau.dev/tulir/mautrix-telegram:v0.8.2. We override here
+            // fields that we know are strictly necessary to be configured when
+            // instantiating this template.
+            config: (std.native("parseYaml")(importstr "appservice-telegram.yaml")[0]) + {
+                homeserver+: {
+                    address: error "homeserver.address must be set",
+                    domain: error "homeserver.domain must be set",
+                },
+                appservice+: {
+                    address: bridge.svc.http_url,
+                    // We disable this. I have no idea what it does, but it
+                    // wants a secret. ~q3k
+                    provisioning+: {
+                        enabled: false,
+                        shared_secret: if self.enabled then error "appservice.provisioning.shared_secret must be set" else "hackme",
+                    },
+                    id: error "appservice.id must be set",
+                    as_token: "This value is generated when generating the registration",
+                    hs_token: "This value is generated when generating the registration",
+                },
+                telegram+: {
+                    api_id: error "telegram.api_id must be set",
+                    api_hash: error "telegram.api_hash must be set",
+                    bot_token: error "telegram.bot_token must be set",
+                },
+                bridge+: {
+                    permissions: {
+                        '*': "relaybot",
+                    },
+                },
+            },
+        },
+
+        config: kube.Secret("appservice-telegram-%s" % [name]) {
+            metadata+: cfg.metadata,
+            data: {
+                "config.yaml": std.base64(std.manifestYamlDoc(cfg.config)),
+            },
+        },
+
+        dataVolume: kube.PersistentVolumeClaim("appservice-telegram-%s" % [name]) {
+            metadata+: cfg.metadata,
+            spec+: {
+                storageClassName: cfg.storageClassName,
+                accessModes: [ "ReadWriteOnce" ],
+                resources: {
+                    requests: {
+                        storage: "10Gi",
+                    },
+                },
+            },
+        },
+
+        bootstrapJob: kube.Job("appservice-telegram-%s-bootstrap" % [name]) {
+            metadata+: cfg.metadata {
+                labels: {
+                    "job-name": "appservice-telegram-%s-bootstrap" % [name],
+                },
+            },
+            spec+: {
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.SecretVolume(bridge.config),
+                        },
+                        containers_: {
+                            bootstrap: kube.Container("appservice-telegram-%s-bootstrap" % [name]) {
+                                image: cfg.image,
+                                command: [
+                                    "sh", "-c",
+                                    "python3 -m mautrix_telegram -g -c /config/config.yaml -r /tmp/registration.yaml && echo SNIPSNIP && cat /tmp/registration.yaml",
+                                ],
+                                volumeMounts_: {
+                                    config: { mountPath: "/config" },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+
+        deployment: kube.Deployment("appservice-telegram-%s" % [name]) {
+            metadata+: cfg.metadata,
+            spec+: {
+                replicas: 1,
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.SecretVolume(bridge.config),
+                            data: kube.PersistentVolumeClaimVolume(bridge.dataVolume),
+                            registration: { secret: { secretName: "appservice-telegram-%s-registration" % [name] } },
+                        },
+                        initContainers: [
+                            // This container takes the stateless config from the Secret, and
+                            // updates it with the registration secrets from the registration token.
+                            kube.Container("generate-config") {
+                                volumeMounts_: {
+                                    config: { mountPath: "/config", },
+                                    registration: { mountPath: "/registration", },
+                                    data: { mountPath: "/data" },
+                                },
+                                // Ow, the edge! We need yq.
+                                // See: https://github.com/mikefarah/yq/issues/190#issuecomment-667519015
+                                image: "alpine@sha256:156f59dc1cbe233827642e09ed06e259ef6fa1ca9b2e29d52ae14d5e7b79d7f0",
+                                command: [
+                                    "sh", "-c", |||
+                                        set -e -x
+                                        apk add --no-cache yq
+                                        cp /config/config.yaml /data/config.yaml
+                                        yq w -i /data/config.yaml appservice.as_token $(yq r /registration/registration.yaml as_token)
+                                        yq w -i /data/config.yaml appservice.hs_token $(yq r /registration/registration.yaml hs_token)
+                                    |||
+                                ],
+                            },
+                        ],
+                        containers_: {
+                            appserviceIrc: kube.Container("appservice-telegram-%s" % [name]) {
+                                image: cfg.image,
+                                command: [
+                                    "sh", "-c", |||
+                                        alembic -x config=/data/config.yaml upgrade head
+                                        python3 -m mautrix_telegram -n -c /data/config.yaml
+                                    |||
+                                ],
+                                ports_: {
+                                    http: { containerPort: 29317 },
+                                },
+                                volumeMounts_: {
+                                    data: { mountPath: "/data" },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+
+        svc: kube.Service("appservice-telegram-%s" % [name]) {
+            metadata+: cfg.metadata,
+            target_pod:: bridge.deployment.spec.template,
+        },
+    },
+}
diff --git a/app/matrix/appservice-telegram.yaml b/app/matrix/appservice-telegram.yaml
new file mode 100644
index 0000000..28880af
--- /dev/null
+++ b/app/matrix/appservice-telegram.yaml
@@ -0,0 +1,443 @@
+# Homeserver details
+homeserver:
+    # The address that this appservice can use to connect to the homeserver.
+    address: https://example.com
+    # The domain of the homeserver (for MXIDs, etc).
+    domain: example.com
+    # Whether or not to verify the SSL certificate of the homeserver.
+    # Only applies if address starts with https://
+    verify_ssl: true
+
+# Application service host/registration related details
+# Changing these values requires regeneration of the registration.
+appservice:
+    # The address that the homeserver can use to connect to this appservice.
+    address: http://localhost:29317
+    # When using https:// the TLS certificate and key files for the address.
+    tls_cert: false
+    tls_key: false
+
+    # The hostname and port where this appservice should listen.
+    hostname: 0.0.0.0
+    port: 29317
+    # The maximum body size of appservice API requests (from the homeserver) in mebibytes
+    # Usually 1 is enough, but on high-traffic bridges you might need to increase this to avoid 413s
+    max_body_size: 1
+
+    # The full URI to the database. SQLite and Postgres are fully supported.
+    # Other DBMSes supported by SQLAlchemy may or may not work.
+    # Format examples:
+    #   SQLite:   sqlite:///filename.db
+    #   Postgres: postgres://username:password@hostname/dbname
+    database: sqlite:////data/mautrix-telegram.db
+
+    # Public part of web server for out-of-Matrix interaction with the bridge.
+    # Used for things like login if the user wants to make sure the 2FA password isn't stored in
+    # the HS database.
+    public:
+        # Whether or not the public-facing endpoints should be enabled.
+        enabled: false
+        # The prefix to use in the public-facing endpoints.
+        prefix: /public
+        # The base URL where the public-facing endpoints are available. The prefix is not added
+        # implicitly.
+        external: https://example.com/public
+
+    # Provisioning API part of the web server for automated portal creation and fetching information.
+    # Used by things like Dimension (https://dimension.t2bot.io/).
+    provisioning:
+        # Whether or not the provisioning API should be enabled.
+        enabled: true
+        # The prefix to use in the provisioning API endpoints.
+        prefix: /_matrix/provision/v1
+        # The shared secret to authorize users of the API.
+        # Set to "generate" to generate and save a new token.
+        shared_secret: hackmehackmehackme
+
+    # The unique ID of this appservice.
+    id: telegram
+    # Username of the appservice bot.
+    bot_username: telegrambot
+    # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty
+    # to leave display name/avatar as-is.
+    bot_displayname: Telegram bridge bot
+    bot_avatar: mxc://maunium.net/tJCRmUyJDsgRNgqhOgoiHWbX
+
+    # Community ID for bridged users (changes registration file) and rooms.
+    # Must be created manually.
+    #
+    # Example: "+telegram:example.com". Set to false to disable.
+    community_id: false
+
+    # Authentication tokens for AS <-> HS communication. Autogenerated; do not modify.
+    as_token: This value is generated when generating the registration
+    hs_token: This value is generated when generating the registration
+
+# Prometheus telemetry config. Requires prometheus-client to be installed.
+metrics:
+    enabled: false
+    listen_port: 8000
+
+# Manhole config.
+manhole:
+    # Whether or not opening the manhole is allowed.
+    enabled: false
+    # The path for the unix socket.
+    path: /var/tmp/mautrix-telegram.manhole
+    # The list of UIDs who can be added to the whitelist.
+    # If empty, any UIDs can be specified in the open-manhole command.
+    whitelist:
+    - 0
+
+# Bridge config
+bridge:
+    # Localpart template of MXIDs for Telegram users.
+    # {userid} is replaced with the user ID of the Telegram user.
+    username_template: telegram_{userid}
+    # Localpart template of room aliases for Telegram portal rooms.
+    # {groupname} is replaced with the name part of the public channel/group invite link ( https://t.me/{} )
+    alias_template: telegram_{groupname}
+    # Displayname template for Telegram users.
+    # {displayname} is replaced with the display name of the Telegram user.
+    displayname_template: '{displayname} (Telegram)'
+
+    # Set the preferred order of user identifiers which to use in the Matrix puppet display name.
+    # In the (hopefully unlikely) scenario that none of the given keys are found, the numeric user
+    # ID is used.
+    #
+    # If the bridge is working properly, a phone number or an username should always be known, but
+    # the other one can very well be empty.
+    #
+    # Valid keys:
+    #   "full name"          (First and/or last name)
+    #   "full name reversed" (Last and/or first name)
+    #   "first name"
+    #   "last name"
+    #   "username"
+    #   "phone number"
+    displayname_preference:
+    - full name
+    - username
+    - phone number
+    # Maximum length of displayname
+    displayname_max_length: 100
+    # Remove avatars from Telegram ghost users when removed on Telegram. This is disabled by default
+    # as there's no way to determine whether an avatar is removed or just hidden from some users. If
+    # you're on a single-user instance, this should be safe to enable.
+    allow_avatar_remove: false
+
+    # Maximum number of members to sync per portal when starting up. Other members will be
+    # synced when they send messages. The maximum is 10000, after which the Telegram server
+    # will not send any more members.
+    # Defaults to no local limit (-> limited to 10000 by server)
+    max_initial_member_sync: 100
+    # Whether or not to sync the member list in channels.
+    # If no channel admins have logged into the bridge, the bridge won't be able to sync the member
+    # list regardless of this setting.
+    sync_channel_members: true
+    # Whether or not to skip deleted members when syncing members.
+    skip_deleted_members: true
+    # Whether or not to automatically synchronize contacts and chats of Matrix users logged into
+    # their Telegram account at startup.
+    startup_sync: true
+    # Number of most recently active dialogs to check when syncing chats.
+    # Set to 0 to remove limit.
+    sync_dialog_limit: 30
+    # Whether or not to sync and create portals for direct chats at startup.
+    sync_direct_chats: false
+    # The maximum number of simultaneous Telegram deletions to handle.
+    # A large number of simultaneous redactions could put strain on your homeserver.
+    max_telegram_delete: 10
+    # Whether or not to automatically sync the Matrix room state (mostly unpuppeted displaynames)
+    # at startup and when creating a bridge.
+    sync_matrix_state: true
+    # Allow logging in within Matrix. If false, the only way to log in is using the out-of-Matrix
+    # login website (see appservice.public config section)
+    allow_matrix_login: true
+    # Whether or not to bridge plaintext highlights.
+    # Only enable this if your displayname_template has some static part that the bridge can use to
+    # reliably identify what is a plaintext highlight.
+    plaintext_highlights: false
+    # Whether or not to make portals of publicly joinable channels/supergroups publicly joinable on Matrix.
+    public_portals: true
+    # Whether or not to use /sync to get presence, read receipts and typing notifications when using
+    # your own Matrix account as the Matrix puppet for your Telegram account.
+    sync_with_custom_puppets: true
+    # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth
+    #
+    # If set, custom puppets will be enabled automatically for local users
+    # instead of users having to find an access token and run `login-matrix`
+    # manually.
+    login_shared_secret:
+    # Set to false to disable link previews in messages sent to Telegram.
+    telegram_link_preview: true
+    # Use inline images instead of a separate message for the caption.
+    # N.B. Inline images are not supported on all clients (e.g. Riot iOS).
+    inline_images: false
+    # Maximum size of image in megabytes before sending to Telegram as a document.
+    image_as_file_size: 10
+    # Maximum size of Telegram documents in megabytes to bridge.
+    max_document_size: 100
+    # Enable experimental parallel file transfer, which makes uploads/downloads much faster by
+    # streaming from/to Matrix and using many connections for Telegram.
+    # Note that generating HQ thumbnails for videos is not possible with streamed transfers.
+    parallel_file_transfer: false
+    # Whether or not created rooms should have federation enabled.
+    # If false, created portal rooms will never be federated.
+    federate_rooms: true
+    # Settings for converting animated stickers.
+    animated_sticker:
+        # Format to which animated stickers should be converted.
+        # disable - No conversion, send as-is (gzipped lottie)
+        # png - converts to non-animated png (fastest),
+        # gif - converts to animated gif, but loses transparency
+        # webm - converts to webm video, requires ffmpeg executable with vp9 codec and webm container support
+        target: gif
+        # Arguments for converter. All converters take width and height.
+        # GIF converter takes background as a hex color.
+        args:
+            width: 256
+            height: 256
+            background: '020202'  # only for gif
+            fps: 30               # only for webm
+    # End-to-bridge encryption support options. These require matrix-nio to be installed with pip
+    # and login_shared_secret to be configured in order to get a device for the bridge bot.
+    #
+    # Additionally, https://github.com/matrix-org/synapse/pull/5758 is required if using a normal
+    # application service.
+    encryption:
+        # Allow encryption, work in group chat rooms with e2ee enabled
+        allow: false
+        # Default to encryption, force-enable encryption in all portals the bridge creates
+        # This will cause the bridge bot to be in private chats for the encryption to work properly.
+        default: false
+    # Whether or not to explicitly set the avatar and room name for private
+    # chat portal rooms. This will be implicitly enabled if encryption.default is true.
+    private_chat_portal_meta: false
+    # Whether or not the bridge should send a read receipt from the bridge bot when a message has
+    # been sent to Telegram.
+    delivery_receipts: false
+    # Whether or not delivery errors should be reported as messages in the Matrix room.
+    delivery_error_reports: false
+
+    # Overrides for base power levels.
+    initial_power_level_overrides:
+        user: {}
+        group: {}
+
+    # Whether to bridge Telegram bot messages as m.notices or m.texts.
+    bot_messages_as_notices: true
+    bridge_notices:
+        # Whether or not Matrix bot messages (type m.notice) should be bridged.
+        default: false
+        # List of user IDs for whom the previous flag is flipped.
+        # e.g. if bridge_notices.default is false, notices from other users will not be bridged, but
+        #      notices from users listed here will be bridged.
+        exceptions:
+        - '@importantbot:example.com'
+
+    # Some config options related to Telegram message deduplication.
+    # The default values are usually fine, but some debug messages/warnings might recommend you
+    # change these.
+    deduplication:
+        # Whether or not to check the database if the message about to be sent is a duplicate.
+        pre_db_check: false
+        # The number of latest events to keep when checking for duplicates.
+        # You might need to increase this on high-traffic bridge instances.
+        cache_queue_length: 20
+
+    # The formats to use when sending messages to Telegram via the relay bot.
+    # Text msgtypes (m.text, m.notice and m.emote) support HTML, media msgtypes don't.
+    #
+    # Available variables:
+    #   $sender_displayname - The display name of the sender (e.g. Example User)
+    #   $sender_username    - The username (Matrix ID localpart) of the sender (e.g. exampleuser)
+    #   $sender_mxid        - The Matrix ID of the sender (e.g. @exampleuser:example.com)
+    #   $message            - The message content
+    message_formats:
+        m.text: '<b>$sender_displayname</b>: $message'
+        m.notice: '<b>$sender_displayname</b>: $message'
+        m.emote: '* <b>$sender_displayname</b> $message'
+        m.file: '<b>$sender_displayname</b> sent a file: $message'
+        m.image: '<b>$sender_displayname</b> sent an image: $message'
+        m.audio: '<b>$sender_displayname</b> sent an audio file: $message'
+        m.video: '<b>$sender_displayname</b> sent a video: $message'
+        m.location: '<b>$sender_displayname</b> sent a location: $message'
+    # Telegram doesn't have built-in emotes, this field specifies how m.emote's from authenticated
+    # users are sent to telegram. All fields in message_formats are supported. Additionally, the
+    # Telegram user info is available in the following variables:
+    #    $displayname - Telegram displayname
+    #    $username    - Telegram username (may not exist)
+    #    $mention     - Telegram @username or displayname mention (depending on which exists)
+    emote_format: '* $mention $formatted_body'
+
+    # The formats to use when sending state events to Telegram via the relay bot.
+    #
+    # Variables from `message_formats` that have the `sender_` prefix are available without the prefix.
+    # In name_change events, `$prev_displayname` is the previous displayname.
+    #
+    # Set format to an empty string to disable the messages for that event.
+    state_event_formats:
+        join: <b>$displayname</b> joined the room.
+        leave: <b>$displayname</b> left the room.
+        name_change: <b>$prev_displayname</b> changed their name to <b>$displayname</b>
+
+    # Filter rooms that can/can't be bridged. Can also be managed using the `filter` and
+    # `filter-mode` management commands.
+    #
+    # Filters do not affect direct chats.
+    # An empty blacklist will essentially disable the filter.
+    filter:
+        # Filter mode to use. Either "blacklist" or "whitelist".
+        # If the mode is "blacklist", the listed chats will never be bridged.
+        # If the mode is "whitelist", only the listed chats can be bridged.
+        mode: blacklist
+        # The list of group/channel IDs to filter.
+        list: []
+
+    # The prefix for commands. Only required in non-management rooms.
+    command_prefix: '!tg'
+
+    # Permissions for using the bridge.
+    # Permitted values:
+    #   relaybot - Only use the bridge via the relaybot, no access to commands.
+    #       user - Relaybot level + access to commands to create bridges.
+    #  puppeting - User level + logging in with a Telegram account.
+    #       full - Full access to use the bridge, i.e. previous levels + Matrix login.
+    #      admin - Full access to use the bridge and some extra administration commands.
+    # Permitted keys:
+    #        * - All Matrix users
+    #   domain - All users on that homeserver
+    #     mxid - Specific user
+    permissions:
+        '*': relaybot
+        public.example.com: user
+        example.com: full
+        '@admin:example.com': admin
+    relaybot:
+        private_chat:
+            # List of users to invite to the portal when someone starts a private chat with the bot.
+            # If empty, private chats with the bot won't create a portal.
+            invite: []
+            # Whether or not to bridge state change messages in relaybot private chats.
+            state_changes: true
+            # When private_chat_invite is empty, this message is sent to users /starting the
+            # relaybot. Telegram's "markdown" is supported.
+            message: This is a Matrix bridge relaybot and does not support direct chats
+        # List of users to invite to all group chat portals created by the bridge.
+        group_chat_invite: []
+        # Whether or not the relaybot should not bridge events in unbridged group chats.
+        # If false, portals will be created when the relaybot receives messages, just like normal
+        # users. This behavior is usually not desirable, as it interferes with manually bridging
+        # the chat to another room.
+        ignore_unbridged_group_chat: true
+        # Whether or not to allow creating portals from Telegram.
+        authless_portals: true
+        # Whether or not to allow Telegram group admins to use the bot commands.
+        whitelist_group_admins: true
+        # Whether or not to ignore incoming events sent by the relay bot.
+        ignore_own_incoming_events: true
+        # List of usernames/user IDs who are also allowed to use the bot commands.
+        whitelist:
+        - myusername
+        - 12345678
+
+# Telegram config
+telegram:
+    # Get your own API keys at https://my.telegram.org/apps
+    api_id: 12345
+    api_hash: tjyd5yge35lbodk1xwzw2jstp90k55qz
+    # (Optional) Create your own bot at https://t.me/BotFather
+    bot_token: disabled
+
+    # Telethon connection options.
+    connection:
+        # The timeout in seconds to be used when connecting.
+        timeout: 120
+        # How many times the reconnection should retry, either on the initial connection or when
+        # Telegram disconnects us. May be set to a negative or null value for infinite retries, but
+        # this is not recommended, since the program can get stuck in an infinite loop.
+        retries: 5
+        # The delay in seconds to sleep between automatic reconnections.
+        retry_delay: 1
+        # The threshold below which the library should automatically sleep on flood wait errors
+        # (inclusive). For instance, if a FloodWaitError for 17s occurs and flood_sleep_threshold
+        # is 20s, the library will sleep automatically. If the error was for 21s, it would raise
+        # the error instead. Values larger than a day (86400) will be changed to a day.
+        flood_sleep_threshold: 60
+        # How many times a request should be retried. Request are retried when Telegram is having
+        # internal issues, when there is a FloodWaitError less than flood_sleep_threshold, or when
+        # there's a migrate error. May take a negative or null value for infinite retries, but this
+        # is not recommended, since some requests can always trigger a call fail (such as searching
+        # for messages).
+        request_retries: 5
+
+    # Device info sent to Telegram.
+    device_info:
+        # "auto" = OS name+version.
+        device_model: auto
+        # "auto" = Telethon version.
+        system_version: auto
+        # "auto" = mautrix-telegram version.
+        app_version: auto
+        lang_code: en
+        system_lang_code: en
+
+    # Custom server to connect to.
+    server:
+        # Set to true to use these server settings. If false, will automatically
+        # use production server assigned by Telegram. Set to false in production.
+        enabled: false
+        # The DC ID to connect to.
+        dc: 2
+        # The IP to connect to.
+        ip: 149.154.167.40
+        # The port to connect to. 443 may not work, 80 is better and both are equally secure.
+        port: 80
+
+    # Telethon proxy configuration.
+    # You must install PySocks from pip for proxies to work.
+    proxy:
+        # Allowed types: disabled, socks4, socks5, http, mtproxy
+        type: disabled
+        # Proxy IP address and port.
+        address: 127.0.0.1
+        port: 1080
+        # Whether or not to perform DNS resolving remotely. Only for socks/http proxies.
+        rdns: true
+        # Proxy authentication (optional). Put MTProxy secret in password field.
+        username: ''
+        password: ''
+
+# Python logging configuration.
+#
+# See section 16.7.2 of the Python documentation for more info:
+# https://docs.python.org/3.6/library/logging.config.html#configuration-dictionary-schema
+logging:
+    version: 1
+    formatters:
+        colored:
+            (): mautrix_telegram.util.ColorFormatter
+            format: '[%(asctime)s] [%(levelname)s@%(name)s] %(message)s'
+        normal:
+            format: '[%(asctime)s] [%(levelname)s@%(name)s] %(message)s'
+    handlers:
+        file:
+            class: logging.handlers.RotatingFileHandler
+            formatter: normal
+            filename: ./mautrix-telegram.log
+            maxBytes: 10485760
+            backupCount: 10
+        console:
+            class: logging.StreamHandler
+            formatter: colored
+    loggers:
+        mau:
+            level: DEBUG
+        telethon:
+            level: INFO
+        aiohttp:
+            level: INFO
+    root:
+        level: DEBUG
+        handlers: [file, console]
diff --git a/app/matrix/homeserver.yaml b/app/matrix/homeserver.yaml
index d83dc3d..6152807 100644
--- a/app/matrix/homeserver.yaml
+++ b/app/matrix/homeserver.yaml
@@ -3,6 +3,7 @@
 ## Server ##

 

 server_name: "hackerspace.pl"

+public_baseurl: "https://matrix.hackerspace.pl"

 pid_file: /homeserver.pid

 web_client: False

 soft_file_limit: 0

@@ -23,14 +24,19 @@
       - names: [federation]

         compress: false

 

+  - port: 9092

+    type: metrics

+    bind_addresses: ['::']

+

 ## Database ##

 

 database:

   name: "psycopg2"

   args:

     user: "synapse"

+    password: "{{ POSTGRES_PASSWORD }}"

     database: "synapse"

-    host: "postgres"

+    host: "waw3-postgres"

     port: "5432"

     cp_min: 5

     cp_max: 10

@@ -83,6 +89,7 @@
 ## Registration ##

 

 enable_registration: False

+registration_shared_secret: "{{ SYNAPSE_REGISTRATION_SHARED_SECRET }}"

 bcrypt_rounds: 12

 allow_guest_access: True

 enable_group_creation: true

@@ -111,7 +118,15 @@
     - "m.room.name"

 

 

-app_service_config_files: ["/appservices/irc-freenode/registration.yaml"]

+{% if SYNAPSE_APPSERVICES %}

+app_service_config_files:

+{% for appservice in SYNAPSE_APPSERVICES %}    - "{{ appservice }}"

+{% endfor %}

+{% else %}

+app_service_config_files: []

+{% endif %}

+

+macaroon_secret_key: "{{ SYNAPSE_MACAROON_SECRET_KEY }}"

 expire_access_token: False

 

 ## Signing Keys ##

@@ -129,7 +144,7 @@
           key: "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"

 

 password_config:

-   enabled: true

+   enabled: false

 

 cas_config:

   enabled: true

diff --git a/app/matrix/prod.jsonnet b/app/matrix/prod.jsonnet
index 736cf87..25f943f 100644
--- a/app/matrix/prod.jsonnet
+++ b/app/matrix/prod.jsonnet
@@ -2,11 +2,26 @@
 # This needs a secret provisioned, create with:
 #    kubectl -n matrix create secret generic synapse --from-literal=postgres_password=$(pwgen 24 1) --from-literal=macaroon_secret_key=$(pwgen 32 1) --from-literal=registration_shared_secret=$(pwgen 32 1)
 #    kubectl -n matrix create secret generic oauth2-cas-proxy --from-literal=oauth2_secret=...
+#
+# Sequencing appservices is fun. The appservice needs to run first (for
+# instance, via a bootstrap job), and on startup it will spit out a
+# registration file.  This registration file then needs to be fed to synapse -
+# this is done via specialy named secrets (appservice-X-registration, for X key
+# in the appservices object).
+#
+# For appservice-irc instances, you can use this oneliner magic to get the
+# registration YAML from logs.
 #    kubectl -n matrix create secret generic appservice-irc-freenode-registration --from-file=registration.yaml=<(kubectl logs -n matrix $(kubectl get pods -n matrix --selector=job-name=appservice-irc-freenode-bootstrap --output=jsonpath='{.items[*].metadata.name}') | tail -n +4 | sed -r 's/(.*aliases:.*)/      group_id: "+freenode:hackerspace.pl"\n\1/')
+#
+# For appservice-telegram instances, you can use this oneliner magic:
+#    kubectl -n matrix create secret generic appservice-telegram-prod-registration --from-file=registration.yaml=<(kubectl -n matrix logs job/appservice-telegram-prod-bootstrap | grep -A 100 SNIPSNIP | grep -v SNIPSNIP)
 
 local kube = import "../../kube/kube.libsonnet";
 local postgres = import "../../kube/postgres.libsonnet";
 
+local irc = import "appservice-irc.libsonnet";
+local telegram = import "appservice-telegram.libsonnet";
+
 {
     local app = self,
     local cfg = app.cfg,
@@ -14,11 +29,14 @@
         namespace: "matrix",
         domain: "matrix.hackerspace.pl",
         serverName: "hackerspace.pl",
-        storageClassName: "waw-hdd-paranoid-2",
+        storageClassName: "waw-hdd-redundant-3",
 
-        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/q3k/oauth2-cas-proxy:0.1.4"
+        synapseImage: "matrixdotorg/synapse:v1.19.2",
+        riotImage: "vectorim/riot-web:v1.7.7",
+        casProxyImage: "registry.k0.hswaw.net/q3k/oauth2-cas-proxy:0.1.4",
+        appserviceIRCImage: "matrixdotorg/matrix-appservice-irc:release-0.17.1",
+        # That's v0.8.2 - we just don't trust that host to not re-tag images.
+        appserviceTelegramImage: "dock.mau.dev/tulir/mautrix-telegram@sha256:9e68eaa80c9e4a75d9a09ec92dc4898b12d48390e01efa4de40ce882a6f7e330"
     },
 
     metadata(component):: {
@@ -32,18 +50,20 @@
 
     namespace: kube.Namespace(cfg.namespace),
 
-    postgres: postgres {
+    postgres3: postgres {
         cfg+: {
             namespace: cfg.namespace,
             appName: "synapse",
             database: "synapse",
             username: "synapse",
+            prefix: "waw3-",
             password: { secretKeyRef: { name: "synapse", key: "postgres_password" } },
             storageClassName: cfg.storageClassName,
+            storageSize: "100Gi",
         },
     },
 
-    dataVolume: kube.PersistentVolumeClaim("synapse-data") {
+    dataVolume: kube.PersistentVolumeClaim("synapse-data-waw3") {
         metadata+: app.metadata("synapse-data"),
         spec+: {
             storageClassName: cfg.storageClassName,
@@ -102,7 +122,7 @@
                 spec+: {
                     volumes_: {
                         data: kube.PersistentVolumeClaimVolume(app.dataVolume),
-                        config: kube.ConfigMapVolume(app.synapseConfig),
+                        config_template: kube.ConfigMapVolume(app.synapseConfig),
                     } + {
                         [k]: { secret: { secretName: "appservice-%s-registration" % [k] } }
                         for k in std.objectFields(app.appservices)
@@ -110,20 +130,29 @@
                     containers_: {
                         web: kube.Container("synapse") {
                             image: cfg.synapseImage,
+                            command: ["/bin/sh", "-c", "/start.py migrate_config && exec /start.py"],
                             ports_: {
                                 http: { containerPort: 8008 },
+                                metrics: { containerPort: 9092 },
                             },
                             env_: {
+                                SYNAPSE_CONFIG_DIR: "/config",
                                 SYNAPSE_CONFIG_PATH: "/config/homeserver.yaml",
 
-                                SYNAPSE_macaroon_secret_key: { secretKeyRef: { name: "synapse", key: "macaroon_secret_key" } },
-                                SYNAPSE_registration_shared_secret: { secretKeyRef: { name: "synapse", key: "registration_shared_secret" } },
-                                SYNAPSE_database__args__password: { secretKeyRef: { name: "synapse", key: "postgres_password" } },
+                                # These values are not used in a template, but
+                                # are required by /start.py migrate_config
+                                SYNAPSE_SERVER_NAME: "hackerspace.pl",
+                                SYNAPSE_REPORT_STATS: "no",
+
+                                SYNAPSE_MACAROON_SECRET_KEY: { secretKeyRef: { name: "synapse", key: "macaroon_secret_key" } },
+                                SYNAPSE_REGISTRATION_SHARED_SECRET: { secretKeyRef: { name: "synapse", key: "registration_shared_secret" } },
+                                POSTGRES_PASSWORD: { secretKeyRef: { name: "synapse", key: "postgres_password" } },
                             },
                             volumeMounts_: {
                                 data: { mountPath: "/data" },
-                                config: {
-                                    mountPath: "/config",
+                                config_template: {
+                                    mountPath: "/conf/homeserver.yaml",
+                                    subPath: "homeserver.yaml",
                                 },
                             } + {
                                 [k]: { mountPath: "/appservices/%s" % [k] }
@@ -193,7 +222,7 @@
                             },
                             volumeMounts_: {
                                 config: {
-                                    mountPath: "/etc/riot-web/config.json",
+                                    mountPath: "/app/config.json",
                                     subPath: "config.json",
                                 },
                             },
@@ -209,13 +238,22 @@
         target_pod:: app.riotDeployment.spec.template,
     },
 
+    // Any appservice you add here will require an appservice-X-registration
+    // secret containing a registration.yaml file. Adding something to this
+    // dictionary will cause Synapse to not start until that secret is
+    // available - so change things carefully!
+    // If bootstrapping a new appservice, just keep it out of this dictionary
+    // until it spits you a registration YAML and you feed that to a secret.
     appservices: {
-        "irc-freenode": app.AppServiceIrc("freenode") {
+        "irc-freenode": irc.AppServiceIrc("freenode") {
             cfg+: {
+                image: cfg.appserviceIRCImage,
+                // TODO(q3k): move this appservice to waw-hdd-redundant-3
+                storageClassName: "waw-hdd-paranoid-2",
                 metadata: app.metadata("appservice-irc-freenode"),
                 // TODO(q3k): add labels to blessed nodes
                 nodeSelector: {
-                    "kubernetes.io/hostname": "bc01n02.hswaw.net",
+                    "kubernetes.io/hostname": "bc01n03.hswaw.net",
                 },
                 config+: {
                     homeserver+: {
@@ -234,6 +272,34 @@
                 },
             },
         },
+        "telegram-prod": telegram.AppServiceTelegram("prod") {
+            cfg+: {
+                image: cfg.appserviceTelegramImage,
+                storageClassName: cfg.storageClassName,
+                metadata: app.metadata("appservice-telegram-prod"),
+
+                config+: {
+                    homeserver+: {
+                        address: "https://%s" % [cfg.domain],
+                        domain: cfg.serverName,
+                    },
+                    appservice+: {
+                        id: "telegram",
+                    },
+                    telegram+: {
+                        api_id: (std.split(importstr "secrets/plain/appservice-telegram-prod-api-id", "\n"))[0],
+                        api_hash: (std.split(importstr "secrets/plain/appservice-telegram-prod-api-hash", "\n"))[0],
+                        bot_token: (std.split(importstr "secrets/plain/appservice-telegram-prod-token", "\n"))[0],
+                    },
+                    bridge+: {
+                        permissions+: {
+                            "hackerspace.pl": "puppeting",
+                            "@q3k:hackerspace.pl": "admin",
+                        },
+                    },
+                },
+            },
+        },
     },
 
     ingress: kube.Ingress("matrix") {
@@ -266,96 +332,4 @@
         },
     },
 
-    AppServiceIrc(name):: {
-        local bridge = self,
-        local cfg = bridge.cfg,
-        cfg:: {
-            image: "registry.k0.hswaw.net/informatic/matrix-appservice-irc:0.12.0",
-            metadata: {},
-            config: std.native("parseYaml")(importstr "appservice-irc.yaml")[0],
-            storageClassName: app.cfg.storageClassName,
-        },
-
-        config: kube.ConfigMap("appservice-irc-%s" % [name]) {
-            metadata+: cfg.metadata,
-            data: {
-                "config.yaml": std.manifestJsonEx(cfg.config, ""),
-            },
-        },
-
-        dataVolume: kube.PersistentVolumeClaim("appservice-irc-%s" % [name]) {
-            metadata+: cfg.metadata,
-            spec+: {
-                storageClassName: cfg.storageClassName,
-                accessModes: [ "ReadWriteOnce" ],
-                resources: {
-                    requests: {
-                        storage: "10Gi",
-                    },
-                },
-            },
-        },
-
-        bootstrapJob: kube.Job("appservice-irc-%s-bootstrap" % [name]) {
-            metadata+: cfg.metadata {
-                labels: {
-                    "job-name": "appservice-irc-%s-bootstrap" % [name],
-                },
-            },
-            spec+: {
-                template+: {
-                    spec+: {
-                        volumes_: {
-                            config: kube.ConfigMapVolume(bridge.config),
-                        },
-                        containers_: {
-                            bootstrap: kube.Container("appservice-irc-%s-bootstrap" % [name]) {
-                                image: cfg.image,
-                                command: ["sh", "-c", "matrix-appservice-irc -r -u http://appservice-irc-%s:9999 -c /config/config.yaml -f /tmp/registration.yaml && cat /tmp/registration.yaml" % [name]],
-                                volumeMounts_: {
-                                    config: { mountPath: "/config" },
-                                },
-                            },
-                        },
-                    },
-                },
-            },
-        },
-
-        deployment: kube.Deployment("appservice-irc-%s" % [name]) {
-            metadata+: cfg.metadata,
-            spec+: {
-                replicas: 1,
-                template+: {
-                    spec+: {
-                        volumes_: {
-                            config: kube.ConfigMapVolume(bridge.config),
-                            data: kube.PersistentVolumeClaimVolume(bridge.dataVolume),
-                            registration: { secret: { secretName: "appservice-irc-%s-registration" % [name] } },
-                        },
-                        nodeSelector: cfg.nodeSelector,
-                        containers_: {
-                            appserviceIrc: kube.Container("appservice-irc-%s" % [name]) {
-                                image: cfg.image,
-                                command: ["matrix-appservice-irc", "-c", "/config/config.yaml", "-f", "/registration/registration.yaml", "-p", "9999"],
-                                ports_: {
-                                    http: { containerPort: 9999 },
-                                },
-                                volumeMounts_: {
-                                    registration: { mountPath: "/registration", },
-                                    config: { mountPath: "/config", },
-                                    data: { mountPath: "/data" },
-                                },
-                            },
-                        },
-                    },
-                },
-            },
-        },
-
-        svc: kube.Service("appservice-irc-%s" % [name]) {
-            metadata+: cfg.metadata,
-            target_pod:: bridge.deployment.spec.template,
-        },
-    },
 }
diff --git a/app/matrix/secrets/cipher/appservice-telegram-prod-api-hash b/app/matrix/secrets/cipher/appservice-telegram-prod-api-hash
new file mode 100644
index 0000000..6328ef9
--- /dev/null
+++ b/app/matrix/secrets/cipher/appservice-telegram-prod-api-hash
@@ -0,0 +1,40 @@
+-----BEGIN PGP MESSAGE-----
+
+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
+=A4un
+-----END PGP MESSAGE-----
diff --git a/app/matrix/secrets/cipher/appservice-telegram-prod-api-id b/app/matrix/secrets/cipher/appservice-telegram-prod-api-id
new file mode 100644
index 0000000..45273fd
--- /dev/null
+++ b/app/matrix/secrets/cipher/appservice-telegram-prod-api-id
@@ -0,0 +1,40 @@
+-----BEGIN PGP MESSAGE-----
+
+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
+b9sc0R49c51gYZ2y00PvXQ==
+=ezNx
+-----END PGP MESSAGE-----
diff --git a/app/matrix/secrets/cipher/appservice-telegram-prod-token b/app/matrix/secrets/cipher/appservice-telegram-prod-token
new file mode 100644
index 0000000..52708e3
--- /dev/null
+++ b/app/matrix/secrets/cipher/appservice-telegram-prod-token
@@ -0,0 +1,41 @@
+-----BEGIN PGP MESSAGE-----
+
+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
+6uaFMmg9
+=xl1i
+-----END PGP MESSAGE-----
diff --git a/app/matrix/secrets/plain/.gitignore b/app/matrix/secrets/plain/.gitignore
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/app/matrix/secrets/plain/.gitignore
@@ -0,0 +1 @@
+*
diff --git a/app/onlyoffice/README.md b/app/onlyoffice/README.md
new file mode 100644
index 0000000..3a48441
--- /dev/null
+++ b/app/onlyoffice/README.md
@@ -0,0 +1,10 @@
+ONLYOFFICE Document Server
+==========================
+
+Production running at office.hackerspace.pl.
+
+JWT secret kept in Kubernetes secrets. Can work with any nextcloud instance as long as the JWT secret is configured correctly.
+
+Has a volume for some persistent data - but this is mostly for caching. As far as I (q3k) undestand, these can be nuked with no repercussions, maybe apart from losing in flight edits.
+
+See [prod.jsonnet](prod.jsonnet) for more information.
diff --git a/app/onlyoffice/prod.jsonnet b/app/onlyoffice/prod.jsonnet
new file mode 100644
index 0000000..927d294
--- /dev/null
+++ b/app/onlyoffice/prod.jsonnet
@@ -0,0 +1,113 @@
+// ONLYOFFICE document server.
+// JWT secret needs to be generated as follows per environment:
+//     kubectl -n onlyoffice-prod create secret generic documentserver-jwt --from-literal=jwt=$(pwgen 32 1)
+
+local kube = import "../../kube/kube.libsonnet";
+local policies = import "../../kube/policies.libsonnet";
+
+{
+    onlyoffice:: {
+        local oo = self,
+        local cfg = oo.cfg,
+        cfg:: {
+            namespace: error "cfg.namespace must be set",
+            image: "onlyoffice/documentserver:5.6.4.20",
+            storageClassName: "waw-hdd-redundant-3",
+            domain: error "cfg.domain must be set",
+        },
+
+        ns: kube.Namespace(cfg.namespace),
+
+        pvc: oo.ns.Contain(kube.PersistentVolumeClaim("documentserver")) {
+            spec+: {
+                storageClassName: cfg.storageClassName,
+                accessModes: [ "ReadWriteOnce" ],
+                resources: {
+                    requests: {
+                        storage: "10Gi",
+                    },
+                },
+            },
+        },
+
+        deploy: oo.ns.Contain(kube.Deployment("documentserver")) {
+            spec+: {
+                template+: {
+                    spec+: {
+                        containers_: {
+                            documentserver: kube.Container("default") {
+                                image: cfg.image,
+                                resources: {
+                                    requests: { memory: "4G", cpu: "100m" },
+                                    limits: { memory: "8G", cpu: "2" },
+                                },
+                                env_: {
+                                    JWT_ENABLED: "true",
+                                    JWT_SECRET: { secretKeyRef: { name: "documentserver-jwt", key: "jwt", }},
+                                },
+                                ports_: {
+                                    http: { containerPort: 80 },
+                                },
+                                local make(sp, p) = { name: "data", mountPath: p, subPath: sp },
+                                volumeMounts: [
+                                    // Per upstream Dockerfile:
+                                    // VOLUME /var/log/$COMPANY_NAME /var/lib/$COMPANY_NAME 
+                                    //        /var/www/$COMPANY_NAME/Data /var/lib/postgresql
+                                    //        /var/lib/rabbitmq /var/lib/redis
+                                    //        /usr/share/fonts/truetype/custom
+                                    make("log", "/var/log/onlyoffice"),
+                                    make("www-data", "/var/www/onlyoffice/Data"),
+                                    make("postgres", "/var/lib/postgresql"),
+                                    make("rabbit", "/var/lib/rabbitmq"),
+                                    make("redis", "/var/lib/redis"),
+                                    make("fonts", "/usr/share/fonts/truetype/custom"),
+                                ],
+                            },
+                        },
+                        volumes_: {
+                            data: kube.PersistentVolumeClaimVolume(oo.pvc),
+                        },
+                    },
+                },
+            },
+        },
+
+        svc: oo.ns.Contain(kube.Service("documentserver")) {
+            target_pod:: oo.deploy.spec.template,
+        },
+        
+        ingress: oo.ns.Contain(kube.Ingress("office")) {
+            metadata+: {
+                annotations+: {
+                    "kubernetes.io/tls-acme": "true",
+                    "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
+                },
+            },
+            spec+: {
+                tls: [{ hosts: [cfg.domain], secretName: "office-tls" }],
+                rules: [
+                    {
+                        host: cfg.domain,
+                        http: {
+                            paths: [
+                                { path: "/", backend: oo.svc.name_port, },
+                            ],
+                        },
+                    },
+                ],
+            },
+        },
+
+        // Needed because the documentserver runs its own supervisor, and:
+        //  - rabbitmq wants to mkdir in /run, which starts out with the wrong permissions
+        //  - nginx wants to bind to port 80
+        insecure: policies.AllowNamespaceInsecure(cfg.namespace),
+    },
+
+    prod: self.onlyoffice {
+        cfg+: {
+            namespace: "onlyoffice-prod",
+            domain: "office.hackerspace.pl",
+        },
+    },
+}
diff --git a/bgpwtf/OWNERS b/bgpwtf/OWNERS
new file mode 100644
index 0000000..4960455
--- /dev/null
+++ b/bgpwtf/OWNERS
@@ -0,0 +1,3 @@
+inherited: false
+owners:
+  - q3k
diff --git a/bgpwtf/cccampix/BUILD b/bgpwtf/cccampix/BUILD
index a96f739..205289f 100644
--- a/bgpwtf/cccampix/BUILD
+++ b/bgpwtf/cccampix/BUILD
@@ -1,5 +1,6 @@
 load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push")
 load("@subpar//:subpar.bzl", "par_binary")
+load("@pydeps//:requirements.bzl", "requirement")
 
 par_binary(
     name = "ripe-sync",
@@ -7,9 +8,9 @@
         "ripe-sync.py",
     ],
     deps = [
-        "@pydeps//grpcio",
-        "@pydeps//requests",
+        requirement("requests"),
         "//bgpwtf/cccampix/proto:ix_py_proto",
+        "//bgpwtf/cccampix/proto:ix_grpc_proto",
     ],
     legacy_create_init = False,
     zip_safe = False,
diff --git a/bgpwtf/cccampix/frontend/BUILD.bazel b/bgpwtf/cccampix/frontend/BUILD.bazel
index 6fae1d3..f9bbd90 100644
--- a/bgpwtf/cccampix/frontend/BUILD.bazel
+++ b/bgpwtf/cccampix/frontend/BUILD.bazel
@@ -1,4 +1,5 @@
 load("@subpar//:subpar.bzl", "par_binary")
+load("@pydeps//:requirements.bzl", "requirement")
 
 py_library(
     name = "frontend_lib",
@@ -10,10 +11,12 @@
         "templates/**",
     ]),
     deps = [
-        "@pydeps//arrow",
-        "@pydeps//flask",
-        "@pydeps//grpcio",
+        requirement("arrow"),
+        requirement("flask"),
+        requirement("werkzeug"),
+        requirement("itsdangerous"),
         "//bgpwtf/cccampix/proto:ix_py_proto",
+        "//bgpwtf/cccampix/proto:ix_grpc_proto",
     ],
 )
 
@@ -36,13 +39,14 @@
     ],
     deps = [
         ":frontend_lib",
-        "@pydeps//gevent",
-        "@pydeps//gunicorn",
+        requirement("gevent"),
+        requirement("gunicorn"),
+        requirement("greenlet"),
     ],
     visibility = [
         "//bgpwtf/cccampix:__pkg__",
     ],
     legacy_create_init = False,
     zip_safe = False,
-    no_remove = True,
+    #no_remove = True,
 )
diff --git a/bgpwtf/cccampix/proto/BUILD.bazel b/bgpwtf/cccampix/proto/BUILD.bazel
index 22c557e..740fef7 100644
--- a/bgpwtf/cccampix/proto/BUILD.bazel
+++ b/bgpwtf/cccampix/proto/BUILD.bazel
@@ -1,6 +1,7 @@
 load("@io_bazel_rules_go//go:def.bzl", "go_library")
 load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
-load("@build_stack_rules_proto//python:python_grpc_compile.bzl", "python_grpc_compile")
+load("@com_github_grpc_grpc//bazel:python_rules.bzl", "py_proto_library", "py_grpc_library")
+
 
 proto_library(
     name = "ix_proto",
@@ -23,16 +24,15 @@
     visibility = ["//visibility:public"],
 )
 
-python_grpc_compile(
-    name = "ix_py_proto_src",
+py_proto_library(
+    name = "ix_py_proto",
     deps = [":ix_proto"],
+    visibility = ["//visibility:public"],
 )
 
-py_library(
-    name = "ix_py_proto",
-    srcs = ["ix_py_proto_src"],
+py_grpc_library(
+    name = "ix_grpc_proto",
+    srcs = [":ix_proto"],
+    deps = [":ix_py_proto"],
     visibility = ["//visibility:public"],
-    deps = [
-        "@pydeps//protobuf",
-    ],
 )
diff --git a/bgpwtf/invoice/templates/invoice_en.html b/bgpwtf/invoice/templates/invoice_en.html
index d661732..6a92022 100644
--- a/bgpwtf/invoice/templates/invoice_en.html
+++ b/bgpwtf/invoice/templates/invoice_en.html
@@ -142,7 +142,7 @@
                     {{ if .USCustomer }}
                     <li>EIN: {{ .InvoiceeVAT }}</li>
                     <li><b>(VAT zero rate)</b></li>
-                    {{ else }}
+                    {{ else if .InvoiceeVAT }}
                     <li><b>NIP:</b> {{ .InvoiceeVAT }}</li>
                     {{ end }}
 
diff --git a/bgpwtf/invoice/templates/invoice_pl.html b/bgpwtf/invoice/templates/invoice_pl.html
index df49da8..15f5d08 100644
--- a/bgpwtf/invoice/templates/invoice_pl.html
+++ b/bgpwtf/invoice/templates/invoice_pl.html
@@ -143,7 +143,7 @@
                     {{ if .USCustomer }}
                     <li>EIN: {{ .InvoiceeVAT }}</li>
                     <li><b>(VAT zero rate)</b></li>
-                    {{ else }}
+                    {{ else if .InvoiceeVAT }}
                     <li><b>NIP:</b> {{ .InvoiceeVAT }}</li>
                     {{ end }}
 
diff --git a/bgpwtf/landing/README.md b/bgpwtf/landing/README.md
new file mode 100644
index 0000000..cc7c44a
--- /dev/null
+++ b/bgpwtf/landing/README.md
@@ -0,0 +1,8 @@
+bgpwtf landing page
+===================
+
+This is what runs on https://bgp.wtf .
+
+It's currently hosted on GAE.
+
+    TODO(q3k): GAE deployment instructions, or move to kube?
diff --git a/bgpwtf/landing/app.yaml b/bgpwtf/landing/app.yaml
new file mode 100644
index 0000000..7bd3e61
--- /dev/null
+++ b/bgpwtf/landing/app.yaml
@@ -0,0 +1,18 @@
+runtime: python27
+api_version: 1
+threadsafe: true
+
+handlers:
+- url: /i
+  static_dir: i
+  secure: always
+
+- url: /.*
+  script: main.app
+  secure: always
+
+libraries:
+- name: webapp2
+  version: latest
+- name: jinja2
+  version: latest
diff --git a/bgpwtf/landing/i/epix.png b/bgpwtf/landing/i/epix.png
new file mode 100644
index 0000000..00fdd73
--- /dev/null
+++ b/bgpwtf/landing/i/epix.png
Binary files differ
diff --git a/bgpwtf/landing/i/nitronet.png b/bgpwtf/landing/i/nitronet.png
new file mode 100644
index 0000000..11f64bc
--- /dev/null
+++ b/bgpwtf/landing/i/nitronet.png
Binary files differ
diff --git a/bgpwtf/landing/i/pepper.png b/bgpwtf/landing/i/pepper.png
new file mode 100644
index 0000000..16b8e78
--- /dev/null
+++ b/bgpwtf/landing/i/pepper.png
Binary files differ
diff --git a/bgpwtf/landing/index.html b/bgpwtf/landing/index.html
new file mode 100644
index 0000000..bad0c19
--- /dev/null
+++ b/bgpwtf/landing/index.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>bgp.wtf</title>
+        <style type="text/css">
+            body {
+                margin: 80px auto;
+                line-height: 1.6;
+                font-size: 18px;
+                max-width: 650px;
+                color: #444;
+                background-color: #eee;
+                padding: 0 10px;
+                font-family: helvetica, arial, sans-serif;
+            }
+            a {
+                color: #444;
+                font-weight: 600;
+                text-decoration: none;
+            }
+            a:hover {
+                text-decoration: underline;
+            }
+            span.snippet {
+                font-family: system, courier new, serif;
+            }
+            div.container {
+                background-color: #f8f8f8;
+                padding: 10px 20px 10px 20px;
+                box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+                margin-bottom: 40px;
+            }
+            div.container h1, h2, h3 {
+                margin: 0;
+            }
+            div.logos {
+                text-align: center;
+            }
+            div.logos img {
+                height: 100px;
+                margin-left: 10px;
+                margin-right: 10px;
+            }
+            div.pepper {
+                width: 128px;
+                height: 128px;
+                background-image: url('i/pepper.png');
+                position: absolute;
+                bottom: 100px;
+                right: 100px;
+                opacity: 0.05;
+            }
+            div.pepper:hover {
+                opacity: 1.0;
+            }
+        </style>
+    </head>
+    <body>
+        <div class="container">
+            <h1>bgp.wtf</h1>
+            <p>
+                The non-profit ISP arm of the <a href="https://hackerspace.pl/">Warsaw Hackerspace</a>. Doing weird stuff on the Internet since 2017.
+            </p>
+            <p>
+                We offer <a href="https://internet.hackerspace.pl/">Internet connectivity in and around Wolność 2A in Warsaw</a>.
+            </p>
+            <p>
+                We peer. Meet us at EPIX-WAR, Wolność 2A in Warsaw.
+            </p>
+            <p>
+                Do you want to learn how the Internet works by running your own v6 CDN? Are you curious how it's like to set up a BGP session? Is DN42 too boring? We also sponsor ASNs and lease v6 address space to likely-minded entities around the world!
+            </p>
+        </div>
+        <div class="container">
+            <p>
+                Chat with us on <span class="snippet">#bgpwtf</span> on Freenode, or by writing an email to <span class="snippet">noc</span> at <span class="snippet">hackerspace.pl</span>.
+            </p>
+        </div>
+        <div class="container">
+            <h3>sponsors &amp; partners</h3>
+            <div class="logos">
+                <a href="http://www.nitronet.pl"><img src="i/nitronet.png" alt="Nitronet logo" title="Nitronet"></a>
+                <a href="https://www.epix.net.pl"><img src="i/epix.png" alt="EPIX logo" title="EPIX"></a>
+            </div>
+        </div>
+        <div class="pepper"> </div>
+    </body>
+</html>
diff --git a/bgpwtf/landing/main.py b/bgpwtf/landing/main.py
new file mode 100644
index 0000000..90011db
--- /dev/null
+++ b/bgpwtf/landing/main.py
@@ -0,0 +1,37 @@
+import jinja2
+import webapp2
+import os
+
+JINJA_ENVIRONMENT = jinja2.Environment(
+    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
+    extensions=['jinja2.ext.autoescape'],
+    autoescape=True)
+
+class LandingPage(webapp2.RequestHandler):
+    def get(self):
+        template_values = {}
+        template = JINJA_ENVIRONMENT.get_template('index.html')
+        self.response.write(template.render(template_values))
+
+class Camp19Page(webapp2.RequestHandler):
+    def get(self):
+        self.redirect('https://events.ccc.de/camp/2019/wiki/Village:IXP')
+
+class PricingPage(webapp2.RequestHandler):
+    def get(self):
+        template_values = {}
+        template = JINJA_ENVIRONMENT.get_template('pricing.html')
+        self.response.write(template.render(template_values))
+
+app = webapp2.WSGIApplication([
+    ('/', LandingPage),
+    ('/cccamp19', Camp19Page),
+    ('/ccccamp19', Camp19Page),
+    ('/camp19', Camp19Page),
+    ('/camp', Camp19Page),
+    ('/cccamp', Camp19Page),
+    ('/ccccamp', Camp19Page),
+    ('/pricing', PricingPage),
+    ('/prices', PricingPage),
+    ('/price', PricingPage),
+], debug=False)
diff --git a/bgpwtf/landing/pricing.html b/bgpwtf/landing/pricing.html
new file mode 100644
index 0000000..bea48b0
--- /dev/null
+++ b/bgpwtf/landing/pricing.html
@@ -0,0 +1,148 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>Warsaw Hackerspace Hosting Services</title>
+    <style type="text/css">
+body {
+  background-color: #f8f8f8;
+  font-family: courier, monospace;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+#page {
+  max-width: 50em;
+  font-size: 1.0em;
+  line-height: 1.5em;
+}
+h2 {
+  margin-top: 2.5em;
+}
+h3 {
+  margin-top: 2em;
+}
+    </style>
+  </head>
+  <body>
+    <div id="page">
+      <h1>Warsaw Hackerspace Hosting Services</h1>
+      <div style="float: right"><a href="/?lang=pl">PL</a> | <a href="/?lang=en">EN</a></div>
+      <p>
+        Nine Fives SLA for the masses.
+      </p>
+
+      <h2>What is this?</h2>
+      <p>
+        The <a href="https://hackerspace.pl/about_en">Warsaw Hackerspace</a> is a Non-Profit, Non-Governmental Organization from Warsaw that runs a bunch of infrastructure for its own needs. In order to make back some of our expenses (network connection, cooling, ...), we decided to start renting out some of our services to outside customers. All of the proceeds will go towards the statuatory goals of the Organization: maintaining and expanding our hackerspace, teaching and education, running classes and generally making the world a better place.
+      </p>
+      <p>
+        Give us money, receive rack space or dedicated servers. And most importantly, <b>a warm fuzzy feeling from supporting a hackerspace</b> &lt;3.
+      </p>
+      <p>
+        Remember, we're a Hackerspace. While we try to maintain as high as un uptime as possible, power and Internet interruptions are always an option, as we do not (yet) have any redundancy in place. So yeah, no warranty. Seriously. If we're down for more than 5% of a given month, your bill will be proportionately lowered to match our downtime - we're not monsters, after all.
+      </p>
+      <h2>Ordering</h2>
+      <p>
+        Contact us at <b>noc@hackerspace.pl</b> and tell us who you are and what you need. We'll respond with a final quote and our world famous NO WARRANTY, NO SLA, NO NOTHING agreement. What follows are list prices in EUR, <b>net (add 23% VAT unless you're a registered EU company with a VAT number)</b>.
+      </p>
+      <h2>Colocation</h2>
+      <p>
+        We can throw your hardware into one of our 42U racks. We'll provide you power and a way to monitor and manage it. Bring your own KVM/iKVM, or rely on lazy remote hands. We measure power usage via a kill-a-watt, and you're billed monthly based on the power you use.
+      </p>
+      <p>
+        <b>What you get</b>:
+        <ul>
+          <li>19" rack space <i>(we do not accept non-rack hardware unless it's something really cool)</i></li>
+          <li>230V AC 50Hz via a C14 plug on a 16A circuit</li>
+          <li>1GbE (copper) or 10GbE (SFP+) switch port <i>(includes best effort Internet access)</i></li>
+        </ul>
+        <b>Pricing</b>: €20 per month per rack unit. €0.25 per KWh.
+      </p>
+      <h2>Dedicated Servers</h2>
+      <p>
+        We have some servers that we can rent you out. We'll provide you with a way to manage the power on it, remotely reinstall the operating system. Some of the machines also come with integrated KVM.
+      </p>
+      <p>
+       <h3>BL-BASE</h3>
+        A Dell M610 blade server.
+        <ul>
+          <li>Xeon L5520 @2.27GHz, 8c/16t</li>
+          <li>24GB RAM ECC</li>
+          <li>2x 140GB SAS <i>(used - use RAID1!)</i></li>
+          <li>KVM <i>(power control, console, keyboard, mouse, ISO mount)</i></li>
+          <li>Hardware RAID controller with BBU(PERC)</li>
+          <li>2x Gigabit Ethernet</li>
+        </ul>
+        <b>Pricing</b>: €45 per month.
+      </p>
+      <p>
+       <h3>BL-64-SSD</h3>
+        A souped-up Dell M610 blade server.
+        <ul>
+          <li>Xeon L5520 @2.27GHz, 8c/16t</li>
+          <li>64GB RAM ECC</li>
+          <li>2x 480GB consumer MLC SSD</li>
+          <li>KVM <i>(power control, console, keyboard, mouse, ISO mount)</i></li>
+          <li>Hardware RAID controller with BBU (PERC)</li>
+          <li>2x Gigabit Ethernet</li>
+        </ul>
+        <b>Pricing</b>: €55 per month.
+      </p>
+      <p>
+       <h3>BL-64-HDD</h3>
+        A further souped-up Dell M610 blade server.
+        <ul>
+          <li>Xeon L5520 @2.27GHz, 8c/16t</li>
+          <li>64GB RAM ECC</li>
+          <li>2x 2TB consumer 2.5" HDD</li>
+          <li>KVM <i>(power control, console, keyboard, mouse, ISO mount)</i></li>
+          <li>Hardware RAID controller with BBU (PERC)</li>
+          <li>2x Gigabit Ethernet</li>
+        </ul>
+        <b>Pricing</b>: €65 per month.
+      </p>
+      <p>
+       <h3>KEKTOP</h3>
+        <p>
+          Way back when, the Hackerspace acquired a whole bunch a Nettop-class machines that used to display advertising in Warsaw taxicabs. After christening them as 'kektops' (hey, that was before the world went to shit), we have decided to rack them up in a custom enclosure with power management, and turned them into a perfect VPS-class dedicated server.
+        </p>
+        <p>
+          Even though the seem underpowered at first glance, they are fairly capable - much more so than a Raspberry Pi, for instance. They make perfect little servers for hosting a blog or small service. Just make sure to get two for redundancy :).
+        </p>
+        <ul>
+          <li>Intel Atom @1.60GHz, 1c/2t</li>
+          <li>2GB RAM</li>
+          <li>16GB SSD</li>
+          <li>Gigabit Ethernet</li>
+        </ul>
+        <b>Pricing</b>: €10 per month + Internet.
+      </p>
+      <h2>Internet Access</h2>
+      <p>
+        We provide upstream via <a href="https://bgp.wtf/">bgp.wtf/AS204880</a>, which has decent connectivity with Poland and to the world (upstreams are Level 3, Telia, GTT, RETN, DE-CIX). We also have direct peering with Google (AS15169), and are present in KleyReX and LocIX.
+      </p>
+      <p>
+         Currently our main upstream circuit is 1GbE, with plans to upgrade to 10GbE as soon as enough commitment is purchased by customers.
+      </p>
+      <p>
+        As we're a registered ISP, we are governed by laws requiring us to provide LEAs with identifying information about customers if a warrant is served for illegal activity. Thus, we will need to collect such some personal information from you (name, address, phone number). The Hackerspace is under the jurisdiction of Polish law.
+      </p>
+      <p>
+        We have a zero tolerance policy towards spam. Customers not responsive to abuse@ requests will have their contract immediately terminated.
+      <p>
+        <b>What you get</b>:
+        <ul>
+          <li>A public IPv4 address and a /64 or /48 of IPv6 addresses</li>
+          <li>Reverse DNS</li>
+          <li>Speed burstable to wire speed of the switch/machine</li>
+        </ul>
+        <b>Pricing</b>: €7 for every 10Mbps of Internet bandwidth committment (can be shared across all of your services).
+      </p>
+      <h2>Extra Features</h2>
+      <p>
+        Contact us for any extra features, like BGP routing, VLANs, extra addresses, ASN sponsorship, ...
+      </p>
+    </div>
+  </body>
+</html>
diff --git a/bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix b/bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix
new file mode 100644
index 0000000..d6ed36a
--- /dev/null
+++ b/bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix
@@ -0,0 +1,36 @@
+# Hardware configuration of edge01. Generated by nixos-generate-config.
+
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports =
+    [ (modulesPath + "/installer/scan/not-detected.nix")
+    ];
+
+  boot.initrd.availableKernelModules = [ "ahci" "usb_storage" "usbhid" ];
+  boot.initrd.kernelModules = [ "dm-snapshot" ];
+  boot.kernelModules = [ "kvm-intel" ];
+  boot.extraModulePackages = [ ];
+  system.stateVersion = "20.03";
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+
+  fileSystems."/" =
+    { device = "/dev/disk/by-uuid/b1b85556-8f3b-4695-8ad5-2159c93c76de";
+      fsType = "ext4";
+    };
+
+  fileSystems."/boot" =
+    # Pendrive.
+    # Real boot is at D8BA-345D.
+    { device = "/dev/disk/by-uuid/D8BA-345D";
+      fsType = "vfat";
+    };
+
+  swapDevices =
+    [ { device = "/dev/disk/by-uuid/5dadcff4-fcd4-4e8d-81f6-be68fb630396"; }
+    ];
+
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+}
diff --git a/bgpwtf/machines/edge01.waw.bgp.wtf.nix b/bgpwtf/machines/edge01.waw.bgp.wtf.nix
new file mode 100644
index 0000000..ec45938
--- /dev/null
+++ b/bgpwtf/machines/edge01.waw.bgp.wtf.nix
@@ -0,0 +1,413 @@
+# Main configuration file for edge01.waw.bgp.wtf.
+# This includes everything needed to run the machine, except for hardware
+# configuration, which is defined in //bgpwtf/machines/
+# edge01.waw.bgp.wtf-hardware.nix.
+#
+# Any changes here can be tested in a local NixOS test by running the following:
+#
+#  nix-build -A bgpwtf.machines.tests.edge01-waw
+#
+# To deploy changes, see //ops:machines.nix.
+
+{ config, pkgs, ... }:
+
+with builtins;
+
+let
+  passwords = import ./secrets/plain/passwords.nix;
+
+in rec {
+  networking.hostName = "edge01";
+  networking.domain = "waw.bgp.wtf";
+
+  imports = [
+    ./modules/router.nix
+
+    # Private configuration data - notably, customer data.
+    ./secrets/plain/edge01.waw.bgp.wtf-private.nix
+  ];
+
+  # TODO(q3k): make this generic, move to modules/router.nix.
+  services.unbound = {
+    enable = true;
+    interfaces = [
+      "185.236.240.1"
+      "2a0d:eb00:2137::1"
+      "127.0.0.1"
+    ];
+    allowedAccess = [
+      "185.236.240.0/22"
+      "2a0d:eb00::0/29"
+      "127.0.0.0/8"
+    ];
+    extraConfig = ''
+      outgoing-interface: 185.236.240.1
+      outgoing-interface: 2a0d:eb00:2137::1
+      cache-max-negative-ttl: 30
+
+      # Disable DoH in Firefox
+      local-zone: "use-application-dns.net" static
+
+      # Rejestr Stron Hazardowych.
+      # Populated by the rsh-unbound daemon.
+      include: "/var/lib/unbound/rsh.conf"
+    '';
+  };
+  hscloud.rsh = {
+    enable = true;
+    out = "/var/lib/unbound/rsh.conf";
+  };
+
+  hscloud.renameInterfaces = {
+    # Link to Nitronet CPE.
+    e1-nnet.mac = "ac:1f:6b:1c:d7:ae";
+    # Link to HSWAW Customs.
+    e2-customs.mac = "ac:1f:6b:1c:d7:af";
+    # Link to management switch.
+    e3-mgmt.mac = "ac:1f:6b:1c:d7:b0";
+    # Link to oob1.
+    e4-oob.mac = "ac:1f:6b:1c:d7:b1";
+    e5.mac = "ac:1f:6b:1c:d7:b2";
+    e6.mac = "ac:1f:6b:1c:d7:b3";
+    # Link to dcsw01.hswaw.net
+    e7-dcsw.mac = "ac:1f:6b:1c:db:06";
+    e8.mac = "ac:1f:6b:1c:db:07";
+  };
+  networking.interfaces.e7-dcsw.mtu = 9000;
+
+  networking.vlans = {
+    "vl-globalmix" = { interface = "e1-nnet"; id = 466; };
+    "vl-polmix" = { interface = "e1-nnet"; id = 2486; };
+    "vl-openpeering" = { interface = "e1-nnet"; id = 992; };
+
+    "vl-dcsw-l3" = { interface = "e7-dcsw"; id = 4001; };
+    "vl-dist-l3" = { interface = "e7-dcsw"; id = 3006; };
+
+    # Extra vlans contained in //bgpwtf/machines/secrets/plain/edge01.waw.bgp.wtf-private.nix
+  };
+  networking.interfaces = {
+    lo = {
+      ipv4.addresses = [ { address = "185.236.240.1"; prefixLength = 32; } ];
+      ipv6.addresses = [ { address = "2a0d:eb00:2137::1"; prefixLength = 64; } ];
+    };
+    ## EPIX links via Nitronet.
+    "vl-globalmix" = {
+      ipv4.addresses = [ { address = "185.235.70.45"; prefixLength = 31; } ];
+      ipv6.addresses = [ { address = "2001:67c:778:fd40::b9eb:462d"; prefixLength = 127; } ];
+    };
+    "vl-polmix" = {
+      ipv4.addresses = [ { address = "94.246.185.175"; prefixLength = 31; } ];
+      ipv6.addresses = [ { address = "2001:67c:778:fa40::5ef6:b9af"; prefixLength = 127; } ];
+    };
+    "vl-openpeering" = {
+      ipv4.addresses = [ { address = "89.46.145.61"; prefixLength = 21; } ];
+      ipv6.addresses = [ { address = "2001:678:3ac::313"; prefixLength = 48; } ];
+    };
+
+    ## L3/mgmt links..
+    # To customs.hackerspace.pl.
+    "e2-customs" = {
+      ipv4.addresses = [ { address = "185.236.240.4"; prefixLength = 31; } ];
+      ipv6.addresses = [ { address = "2a0d:eb00:2137:1::2"; prefixLength = 127; } ];
+    };
+    # To mgmt.
+    "e3-mgmt" = {
+      ipv4.addresses = [ { address = "10.10.10.1"; prefixLength = 24; } ];
+    };
+    # To obb1.
+    "e4-oob" = {
+      ipv4.addresses = [ { address = "185.236.240.74"; prefixLength = 29; } ];
+    };
+    # To dcsw01, L3 (BGP).
+    "vl-dcsw-l3" = {
+      mtu = 9000;
+      ipv4.addresses = [ { address = "185.236.240.6"; prefixLength = 31; } ];
+      ipv6.addresses = [ { address = "2a0d:eb00:2137:1::6"; prefixLength = 127; } ];
+    };
+    # To dist02, L3 (BGP).
+    "vl-dist-l3" = {
+      ipv4.addresses = [ { address = "185.236.240.14"; prefixLength = 31; } ];
+      ipv6.addresses = [ { address = "2a0d:eb00:2137:1::a"; prefixLength = 127; } ];
+    };
+
+    # Extra interface configs contained in //bgpwtf/machines/secrets/plain/edge01.waw.bgp.wtf-private.nix
+  };
+
+  hscloud.routing.enable = true;
+  hscloud.routing.routerID = "185.236.240.1";
+  hscloud.routing.asn = 204880;
+  # Use default master4/master6 tables so that `birdc show route` works.
+  hscloud.routing.tables.master.program = true;
+  hscloud.routing.tables.master.programSourceV4 = "185.236.240.1";
+  hscloud.routing.tables.master.programSourceV6 = "2a0d:eb00:2137::1";
+
+  hscloud.routing.extra = ''
+    function net_martian_v4() {
+      return net ~ [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+,
+        127.0.0.0/8+, 224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ];
+    }
+    function net_as204480_waw_v4() {
+      return net ~ [ 185.236.240.0/23+ ];
+    }
+    function net_martian_v6() {
+      return net ~ [ fc00::/7+, fec0::/10+, ::/128-, ::/0{0,15}, ::/0{49,128} ];
+    }
+    function net_as204480_waw_v6() {
+      return net ~ [ 2a0d:eb00::/32 ];
+    }
+
+  '';
+  hscloud.routing.originate = {
+    # WAW prefixes, exposed into internet BGP table.
+    v4.waw = { table = "internet"; address = "185.236.240.0"; prefixLength = 23; };
+    v6.waw = { table = "internet"; address = "2a0d:eb00::"; prefixLength = 32; };
+
+    # Default gateway via us, exposed into aggregated table.
+    v4.default = { table = "aggregate"; address = "0.0.0.0"; prefixLength = 0; };
+    v6.default = { table = "aggregate"; address = "::"; prefixLength = 0; };
+  };
+  hscloud.routing.pipe = let
+    copySourcesToKernel = sources: table: {
+      table = "master";
+      peerTable = table;
+      filterIn = ''
+        ${concatStringsSep "\n" (map (v: "if source = RTS_${v} then accept;") sources)}
+        reject;
+      '';
+    };
+  in {
+    v4."internet_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "internet";
+    v4."aggregate_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "aggregate";
+    v6."internet_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "internet";
+    v6."aggregate_to_kernel" = copySourcesToKernel ["BGP" "OSPF"] "aggregate";
+  };
+
+  hscloud.routing.ospf.v6.main = {
+    area."0.0.0.0".interfaces = {
+      "e2-customs" = {
+        type = "bcast";
+      };
+      "e4-oob" = {
+        type = "bcast";
+        stub = true;
+      };
+    };
+    table = "aggregate";
+    filterIn = ''
+      # hswaw prefix from e2-customs
+      if net ~ [ 2a0d:eb00:4242::/64+ ] then accept;
+      # e2-customs link
+      if net ~ [ 2a0d:eb00:2137:1::2/127+ ] then accept;
+    '';
+  };
+  hscloud.routing.ospf.v4.main = {
+    area."0.0.0.0".interfaces = {
+      "e4-oob" = {
+        type = "bcast";
+        stub = true;
+      };
+    };
+    table = "aggregate";
+    filterIn = ''
+      # e4-oob link
+      if net ~ [ 185.236.240.72/29+ ] then accept;
+    '';
+  };
+
+  hscloud.routing.bgpSessions.v4 = let
+    filterInUpstream = ''
+      if net_martian_v4() then reject;
+      if net_as204480_waw_v4() then reject;
+      accept;
+    '';
+    filterOutUpstream = ''
+      # Accept AS204880-announced prefixes.
+      if (net ~ [ 185.236.240.0/22+ ]) then accept;
+      reject;
+    '';
+  in {
+    "waw_globalmix" = {
+      description = "UPSTREAM EPIX.WAR GlobalMix";
+      table = "internet";
+      local = "185.235.70.45";
+      neighbors = [
+        { address = "185.235.70.44"; asn = 62081; }
+      ];
+      prepend = 2; pref = 100;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    "waw_polmix" = {
+      description = "UPSTREAM EPIX.WAR PolMix";
+      table = "internet";
+      local = "94.246.185.175";
+      neighbors = [
+        { address = "94.246.185.174"; asn = 201054; }
+      ];
+      prepend = 1; pref = 200;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    "waw_openpeering" = {
+      description = "IXP EPIX.WAR OpenPeering";
+      table = "internet";
+      local = "89.46.145.61";
+      neighbors = [
+        { address = "89.46.144.11"; asn = 48850; }
+        { address = "89.46.144.12"; asn = 48850; }
+      ];
+      prepend = 0; pref = 300;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    "waw_google" = {
+      description = "PEER Google AS15169 (EPIX)";
+      table = "internet";
+      local = "89.46.145.61";
+      neighbors = [
+        # TODO(q3k): secretify the password.
+        { address = "89.46.144.185"; asn = 15169; password = passwords."edge01.waw-bgp-google"; }
+      ];
+      prepend = 0; pref = 300;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    # hscloud spine switch (dcsw01.hswaw.net).
+    "waw_hscloud" = {
+      description = "AGGREGATE CUSTOMER hscloud/dcsw01";
+      table = "aggregate";
+      local = "185.236.240.6";
+      asn = 65000;
+      neighbors = [
+        { address = "185.236.240.7"; asn = 65001; }
+      ];
+      filterIn = ''
+        # wieloryb prefix
+        if net ~ [ 185.236.240.8/31+ ] then accept;
+        # dcsw01 l2 general purpose
+        if net ~ [ 185.236.240.24/29+ ] then accept;
+        # hscloud l2 general purpose
+        if net ~ [ 185.236.240.32/28+ ] then accept;
+        # k0 metallb pools
+        if net ~ [ 185.236.240.48/28+, 185.236.240.112/28+ ] then accept;
+        reject;
+      '';
+    };
+    # bgp.wtf internet customer router on W2A, floor 3 (dist02.bgp.wtf).
+    "waw_dist02" = {
+      description = "AGGREGATE CUSTOMER bgpwtf/dist02";
+      table = "aggregate";
+      local = "185.236.240.14";
+      asn = 65000;
+      neighbors = [
+        { address = "185.236.240.15"; asn = 65002; }
+      ];
+      filterIn = ''
+        # dist02 customer routed
+        if net ~ [  185.236.240.80/28+ ] then accept;
+        reject;
+      '';
+    };
+    # backup LTE link to edge01.fra
+    "fra_edge01" = {
+      description = "IBGP edge01.fra";
+      table = "internet";
+      local = "185.236.240.74";
+      direct = true;
+      neighbors = [
+        { address = "185.236.240.75"; asn = 204880; }
+      ];
+      pref = 50;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+  };
+  hscloud.routing.bgpSessions.v6 = let
+    filterInUpstream = ''
+      if net_martian_v6() then reject;
+      if net_as204480_waw_v6() then reject;
+      accept;
+    '';
+    filterOutUpstream = ''
+      # Accept AS204880-announced prefixes.
+      if (net ~ [ 2a0d:eb00::/29+ ]) then accept;
+      reject;
+    '';
+  in {
+    "waw_globalmix" = {
+      description = "UPSTREAM EPIX.WAR GlobalMix";
+      table = "internet";
+      local = "2001:67c:778:fd40::b9eb:462d";
+      neighbors = [
+        { address = "2001:67c:778:fd40::b9eb:462c"; asn = 62081; }
+      ];
+      prepend = 2; pref = 100;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    "waw_polmix" = {
+      description = "UPSTREAM EPIX.WAR PolMix";
+      table = "internet";
+      local = "2001:67c:778:fa40::5ef6:b9af";
+      neighbors = [
+        { address = "2001:67c:778:fa40::5ef6:b9ae"; asn = 201054; }
+      ];
+      prepend = 1; pref = 200;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    "waw_openpeering" = {
+      description = "IXP EPIX.WAR OpenPeering";
+      table = "internet";
+      local = "2001:678:3ac::313";
+      neighbors = [
+        { address = "2001:678:3ac::11"; asn = 48850; }
+        { address = "2001:678:3ac::12"; asn = 48850; }
+      ];
+      prepend = 0; pref = 300;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    "waw_google" = {
+      description = "PEER Google AS15169 (EPIX)";
+      table = "internet";
+      local = "2001:678:3ac::313";
+      neighbors = [
+        { address = "2001:678:3ac::185"; asn = 15169; password = passwords."edge01.waw-bgp-google"; }
+      ];
+      prepend = 0; pref = 300;
+      filterIn = filterInUpstream;
+      filterOut = filterOutUpstream;
+    };
+    # hscloud spine switch (dcsw01.hswaw.net).
+    "waw_hscloud" = {
+      description = "AGGREGATE CUSTOMER dcsw01.hswaw.net";
+      table = "aggregate";
+      local = "2a0d:eb00:2137:1::6";
+      asn = 65000;
+      neighbors = [
+        { address = "2a0d:eb00:2137:1::7"; asn = 65001; }
+      ];
+      filterIn = ''
+        # dcsw01 l2 general purpose
+        if net ~ [ 2a0d:eb00:2137::/48+ ] then accept;
+        reject;
+      '';
+    };
+    # bgp.wtf internet customer router on W2A, floor 3 (dist02.bgp.wtf).
+    "waw_dist02" = {
+      description = "AGGREGATE CUSTOMER dist02.bgp.wtf";
+      table = "aggregate";
+      local = "2a0d:eb00:2137:1::a";
+      asn = 65000;
+      neighbors = [
+        { address = "2a0d:eb00:2137:1::b"; asn = 65002; }
+      ];
+      filterIn = ''
+        # dist02 customers.
+        if net ~ [ 2a0d:eb00:8002::/48 ] then accept;
+        reject;
+      '';
+    };
+  };
+}
diff --git a/bgpwtf/machines/modules/bootstrap.nix b/bgpwtf/machines/modules/bootstrap.nix
new file mode 100644
index 0000000..09f2555
--- /dev/null
+++ b/bgpwtf/machines/modules/bootstrap.nix
@@ -0,0 +1,77 @@
+# Functionality that used to live in bootstrap.hswaw.net, a VM.
+# PXE boot support has been removed and the functionality moved back to
+# edge01.waw.bgp.wtf.
+
+{ config, pkgs, ... }: {
+  networking.bridges.bootstrap.interfaces = [];
+  networking.interfaces.bootstrap.ipv4.addresses = [
+    { address = "185.236.240.18"; prefixLength = 32; }
+  ];
+  services.dhcpd4 = {
+    enable = true;
+    interfaces = [ "bootstrap" "vl-dcsw-l3" ];
+    extraConfig = ''
+      # ISC DHCP is trash. We only use it in relay mode, yet we have to do
+      # this.
+      subnet 185.236.240.18 netmask 255.255.255.255 {}
+
+      subnet 185.236.240.6 netmask 255.255.255.254 {}
+
+      subnet 185.236.240.24 netmask 255.255.255.248 {
+          option routers 185.236.240.25;
+          range 185.236.240.29 185.236.240.30;
+          option domain-name-servers 8.8.8.8;
+      }
+
+      subnet 185.236.240.32 netmask 255.255.255.240 {
+          range 185.236.240.45 185.236.240.46;
+          option routers 185.236.240.33;
+          option domain-name-servers 8.8.8.8;
+      }
+
+      host bc01n01 {
+          hardware ethernet 00:23:ae:fe:83:20;
+          fixed-address 185.236.240.35;
+          option host-name "bc01n01";
+      }
+      host bc01n02 {
+          hardware ethernet 00:23:ae:fe:83:c4;
+          fixed-address 185.236.240.36;
+          option host-name "bc01n02";
+      }
+      host bc01n03 {
+          hardware ethernet 00:23:ae:fe:42:80;
+          fixed-address 185.236.240.37;
+          option host-name "bc01n03";
+      }
+      host boston-packets {
+          hardware ethernet 00:23:ae:fe:45:8c;
+          fixed-address 185.236.240.38;
+          option host-name "boston-packets.hackerspace.pl";
+          #filename "ipxe.efi";
+      }
+      host dcr01s22 {
+          hardware ethernet 90:1b:0e:08:12:b8;
+          fixed-address 185.236.240.39;
+          option host-name "dcr01s22";
+          #filename "ipxe.efi";
+      }
+      host dcr01s24 {
+          hardware ethernet 90:1b:0e:31:bb:6a;
+          fixed-address 185.236.240.40;
+          option host-name "dcr01s24";
+          #filename "ipxe.efi";
+      }
+      host dsctf {
+          hardware ethernet 00:23:ae:fe:45:50;
+          fixed-address 185.236.240.41;
+          option host-name "dsctf";
+      }
+      host dcr03s32b1 {
+          hardware ethernet 02:01:87:4a:9a:b9;
+          fixed-address 185.236.240.26;
+          option host-name "dcr03s32b1";
+      }
+    '';
+  };
+}
diff --git a/bgpwtf/machines/modules/eoip.nix b/bgpwtf/machines/modules/eoip.nix
new file mode 100644
index 0000000..5ce04f3
--- /dev/null
+++ b/bgpwtf/machines/modules/eoip.nix
@@ -0,0 +1,75 @@
+# A small Ethernet-over-IP service implementation.
+# Yes, that's the Mikrotik EoIP implementation. This one is somewhat sketchy
+# (notably, it pumps huge zero-padded frames into tap), so doesn't use it for
+# production. We currently only use it in the edge01.waw test framework to
+# bring vlans across test VMs.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  eoip = pkgs.stdenv.mkDerivation {
+    pname = "eoip";
+    version = "20180119";
+    nativeBuildInputs = with pkgs; [ cmake ];
+    src = pkgs.fetchFromGitHub {
+      owner = "amphineko";
+      repo = "eoiptapd";
+      rev = "5573a905bcbc001b503308665f098e82f451dc33";
+      sha256 = "0np9dzcw5w6jarzdv2yh3mbzz0wgw10sjqyi6pxan4ipr75v1b8s";
+    };
+    installPhase = ''
+      mkdir -p $out/bin
+      cp eoiptapd $out/bin/eoiptapd
+    '';
+  };
+
+  cfg = config.hscloud.eoip;
+
+in {
+  options.hscloud.eoip = {
+    interfaces = mkOption {
+      type = with types; attrsOf (submodule {
+        options = {
+          localV4 = mkOption {
+            type = types.str;
+            description = "Local outer IPv4 address";
+          };
+          remoteV4 = mkOption {
+            type = types.str;
+            description = "Remote outer IPv4 address";
+          };
+          id = mkOption {
+            type = types.int;
+            description = "Tunnel ID";
+          };
+          parent = mkOption {
+            type = types.str;
+            description = "Parent/outer device";
+          };
+        };
+      });
+      description = ''
+        EoIP interfaces to create.
+      '';
+    };
+  };
+
+  config.systemd.services = mapAttrs' (name: value: nameValuePair "${name}-eoip" {
+    wantedBy = [ "network.target" ];
+    wants = [
+      "${name}-netdev.service"
+      "network-addresses-${value.parent}.service"
+    ];
+    after = [
+      "network-addresses-${value.parent}.service"
+    ];
+    serviceConfig = {
+      Type = "simple";
+      ExecStart = "${eoip}/bin/eoiptapd -i ${name} -l ${value.localV4} -r ${value.remoteV4} -t ${toString value.id}";
+      Restart = "always";
+      RestartSec = "1";
+    };
+  }) cfg.interfaces;
+}
diff --git a/bgpwtf/machines/modules/prometheus.nix b/bgpwtf/machines/modules/prometheus.nix
new file mode 100644
index 0000000..704c257
--- /dev/null
+++ b/bgpwtf/machines/modules/prometheus.nix
@@ -0,0 +1,57 @@
+# Prometheus configuration for a BIRD-enabled router.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  nodeExporterPort = 9100;
+  birdExporterPort = 9101;
+
+  birdExporter = pkgs.buildGoModule rec {
+    pname = "bird-exporter";
+    version = "1.2.5";
+    src = pkgs.fetchFromGitHub {
+      owner = "czerwonk";
+      repo = "bird_exporter";
+      rev = version;
+      sha256 = "1qrhncy1f119f5rfgn2d1l6nvapaqkld4zb9bxzdqmmw6kicc7bs";
+    };
+
+    vendorSha256 = null;
+  };
+
+in {
+  systemd.services.bird_exporter = {
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      Type = "simple";
+      ExecStart = "${birdExporter}/bin/bird_exporter -format.new=true -bird.v2=true -web.listen-address=127.0.0.1:${toString birdExporterPort}";
+      Restart = "always";
+      RestartSec = "60";
+    };
+  };
+
+  services.prometheus.exporters.node = {
+    enable = true;
+    listenAddress = "127.0.0.1";
+    port = nodeExporterPort;
+  };
+
+  services.nginx.enable = true;
+  services.nginx.virtualHosts."${config.networking.hostName}.${config.networking.domain}" = let
+    allowMonitoring = ''
+      allow 209.250.231.127; # monitoring.hackerspace.pl
+      deny all;
+    '';
+  in {
+    locations."/metrics-node" = {
+      proxyPass = "http://127.0.0.1:${toString nodeExporterPort}/metrics";
+      extraConfig = allowMonitoring;
+    };
+    locations."/metrics-bird" = {
+      proxyPass = "http://127.0.0.1:${toString birdExporterPort}/metrics";
+      extraConfig = allowMonitoring;
+    };
+  };
+}
diff --git a/bgpwtf/machines/modules/rename-interfaces.nix b/bgpwtf/machines/modules/rename-interfaces.nix
new file mode 100644
index 0000000..bbb5c81
--- /dev/null
+++ b/bgpwtf/machines/modules/rename-interfaces.nix
@@ -0,0 +1,29 @@
+# Sketchy little module to renamei interfaces by MAC.
+# This only works on startup.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hscloud.renameInterfaces;
+in {
+  options.hscloud.renameInterfaces = mkOption {
+    type = with types; attrsOf (submodule {
+      options = {
+        mac = mkOption {
+          type = types.str;
+          description = ''
+            MAC address to match by, in hexadecimal form (ie. ac:1f:6b:1c:d7:ae).
+          '';
+        };
+      };
+    });
+    description = ''
+      Interfaces to rename by property (eg. MAC address).
+    '';
+  };
+
+  config.services.udev.extraRules = concatStringsSep "\n" (mapAttrsToList (n: v: ''
+    ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="${v.mac}", ATTR{addr_assign_type}=="0", NAME="${n}"
+  '') cfg);
+}
diff --git a/cluster/nix/module-base.nix b/bgpwtf/machines/modules/router.nix
similarity index 64%
copy from cluster/nix/module-base.nix
copy to bgpwtf/machines/modules/router.nix
index 266c145..4999401 100644
--- a/cluster/nix/module-base.nix
+++ b/bgpwtf/machines/modules/router.nix
@@ -1,56 +1,54 @@
+# Generic configuration for any bgpwtf router.
+
 { config, pkgs, lib, ... }:
 
-with (( import ./defs-cluster-k0.nix ) config.networking.hostName);
+with builtins;
 
 rec {
-  system.stateVersion = machine.stateVersion;
-  nix.maxJobs = machine.threads;
-
-  boot.loader.grub.enable = true;
-  boot.loader.grub.version = 2;
-  boot.loader.grub.device = machine.diskBoot;
-
-  fileSystems."/" =
-    { device = machine.fsRoot;
-      fsType = "ext4";
-    };
-  swapDevices = [ ];
-
-  boot.kernelPackages = pkgs.linuxPackages_latest;
-  boot.kernelParams = [ "boot.shell_on_fail" ];
-  boot.kernel.sysctl."net.ipv4.conf.all.rp_filter" = "0";
-  boot.kernel.sysctl."net.ipv4.conf.default.rp_filter" = "0";
-  boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "megaraid_sas" "usb_storage" "usbhid" "sd_mod" "sr_mod"  ];
-  boot.kernelModules = [ "kvm-intel" ];
-  boot.extraModulePackages = [];
-  hardware.enableRedistributableFirmware = true;
-
-  time.timeZone = "Europe/Warsaw";
+  imports = [
+    ./routing.nix
+    ./rename-interfaces.nix
+    ./rsh-unbound.nix
+    ./bootstrap.nix
+    ./prometheus.nix
+  ];
 
   environment.systemPackages = with pkgs; [
-    wget vim htop tcpdump
-    rxvt_unicode.terminfo
+    tcpdump htop dstat file strace gdb mtr
+    vim wget curl htop dstat whois bind
+    rxvt_unicode.terminfo dhcpcd efibootmgr
   ];
-  programs.mtr.enable = true;
-
   networking.useDHCP = false;
-  networking.interfaces."${machine.mgmtIf}".useDHCP = true;
+  networking.firewall.enable = false;
+  boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
+  boot.kernel.sysctl."net.ipv4.conf.*.accept_redirects" = 0;
+  boot.kernel.sysctl."net.ipv4.conf.*.send_redirects" = 0;
+  boot.kernel.sysctl."net.ipv4.conf.*.accept_source_route" = 0;
+  boot.kernel.sysctl."net.ipv4.conf.*.proxy_arp" = 0;
+  boot.kernel.sysctl."net.ipv4.conf.*.secure_redirects" = 1;
+  boot.kernel.sysctl."net.ipv4.conf.*.bootp_relay" = 0;
+  boot.kernel.sysctl."net.ipv4.conf.*.arp_filter" = 1;
+  boot.kernel.sysctl."net.ipv4.conf.*.arp_ignore" = 1;
+  boot.kernel.sysctl."net.ipv4.conf.*.arp_announce" = 2;
+  boot.kernel.sysctl."net.ipv4.conf.*.rp_filter" = 0;
+  boot.kernel.sysctl."net.ipv6.conf.*.forwarding" = 1;
+  boot.kernel.sysctl."net.ipv6.conf.*.accept_ra" = 0;
+  boot.kernel.sysctl."net.ipv6.conf.*.autoconf" = 0;
+  boot.kernel.sysctl."net.ipv6.conf.*.router_solicitations" = 0;
 
-  # Instead of using nixpkgs from the root/nixos channel, use pkgs pin from this file.
-  nix.nixPath = [ "nixpkgs=${pkgs.path}" "nixos-config=/etc/nixos/configuration.nix" ];
-
-  # Otherwise fetchGit nixpkgs pin fails.
-  systemd.services.nixos-upgrade.path = [ pkgs.git ];
+  # Use Chrony instead of systemd-timesyncd
+  time.timeZone = "Europe/Warsaw";
+  services.chrony.enable = true;
+  networking.nameservers = [ "8.8.8.8" ];
 
   # Enable the OpenSSH daemon.
   services.openssh.enable = true;
   users.users.root.openssh.authorizedKeys.keys = [
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD4VJXAXEHEXZk2dxNwehneuJcEGkfXG/U7z4fO79vDVIENdedtXQUyLyhZJc5RTEfHhQj66FwIqzl7mzBHd9x9PuDp6QAYXrkVNMj48s6JXqZqBvF6H/weRqFMf4a2TZv+hG8D0kpvmLheCwWAVRls7Jofnp/My+yDd57GMdsbG/yFEf6WPMiOnA7hxdSJSVihCsCSw2p8PD4GhBe8CVt7xIuinhutjm9zYBjV78NT8acjDUfJh0B1ODTjs7nuW1CC4jybSe2j/OU3Yczj4AxRxBNWuFxUq+jBo9BfpbKLh+Tt7re+zBkaicM77KM/oV6943JJxgHNBBOsv9scZE7 q3k@amnesia"
-    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPm+KopMxs7QfATTKJBjCSKwttslx1u3dHl7tuppwN/4 q3k@paranoia"
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG599UildOrAq+LIOQjKqtGMwjgjIxozI1jtQQRKHtCP q3k@mimeomia"
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQb3YQoiYFZLKwvHYKbu1bMqzNeDCAszQhAe1+QI5SLDOotclyY/vFmOReZOsmyMFl71G2d7d+FbYNusUnNNjTxRYQ021tVc+RkMdLJaORRURmQfEFEKbai6QSFTwErXzuoIzyEPK0lbsQuGgqT9WaVnRzHJ2Q/4+qQbxAS34PuR5NqEkmn4G6LMo3OyJ5mwPkCj9lsqz4BcxRaMWFO3mNcwGDfSW+sqgc3E8N6LKrTpZq3ke7xacpQmcG5DU9VO+2QVPdltl9jWbs3gXjmF92YRNOuKPVfAOZBBsp8JOznfx8s9wDgs7RwPmDpjIAJEyoABqW5hlXfqRbTnfnMvuR informatic@InformaticPC"
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGkMgEVwQM8yeuFUYL2TwlJIq9yUNBmHnwce46zeL2PK2CkMz7sxT/om7sp/K5XDiqeD05Nioe+Dr3drP6B8uI33S5NgxPIfaqQsRS+CBEgk6cqFlcdlKETU/DT+/WsdoO173n7mgGeafPInEuQuGDUID0Fl099kIxtqfAhdeZFMM6/szAZEZsElLJ8K6dp1Ni/jmnXCZhjivZH3AZUlnqrmtDG7FY1bgcOfDXAal45LItughGPtrdiigXe9DK2fW3+9DBZZduh5DMJTNlphAZ+nfSrbyHVKUg6WsgMSprur4KdU47q1QwzqqvEj75JcdP1jOWoZi4F6VJDte9Wb9lhD1jGgjxY9O6Gs4CH35bx15W7CN9hgNa0C8NbPJe/fZYIeMZmJ1m7O2xmnYwP8j+t7RNJWu7Pa3Em4mOEXvhBF07Zfq+Ye/4SluoRgADy5eII2x5fFo5EBhInxK0/X8wF6XZvysalVifoCh7T4Edejoi91oAxFgYAxbboXGlod0eEHIi2hla8SM9+IBHOChmgawKBYp2kzAJyAmHNBF+Pah9G4arVCj/axp/SJZDZbJQoI7UT/fJzEtvlb5RWrHXRq+y6IvjpUq4pzpDWW04+9UMqEEXRmhWOakHfEVM9rN8h3aJBflLUBBnh0Z/hVsKNh8bCRHaKtah8TrD9i+wMw== patryk.jakuszew@gmail.com"
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC33naG1ptCvUcRWX9cj9wXM1nW1lyQC4SvMJzWlr9aMD96O8hQ2JMkuIUgUJvorAY02QRplQ2BuoVoVkdkzwjMyi1bL3OdgcKo7Z1yByClGTTocqNJYY0lcUb6EJH8+6e6F9ydrQlSxNzL1uCaA7phZr+yPcmAmWbSfioXn98yXNkE0emHxzJv/nypJY56sDCMC2IXDRd8L2goDtPwgPEW7bWfAQdIFMJ75xOidZOTxJ8eqyXLw/kxY5UlyX66jdoYz1sE5XUHuoQl1AOG9UdlMo0aMhUvP4pX5l7r7EnA9OttKMFB3oWqkVK/R6ynZ52YNOU5BZ9V+Ppaj34W0xNu+p0mbHcCtXYCTrf/OU0hcZDbDaNTjs6Vtcm2wYw9iAKX7Tex+eOMwUwlrlcyPNRV5BTot7lGNYfauHCSIuWJKN4NhCLR/NtVNh4/94eKkPTwJsY6XqDcS7q49wPAs4DAH7BJgsbHPOqygVHrY0YYEfz3Pj0HTxJHQMCP/hQX4fXEGt0BjgoVJbXPAQtPyeg0JuxiUg+b4CgVVfQ6R060MlM1BZzhmh+FY5MJH6nJppS0aHYCvSg8Z68NUlCPKy0jpcyfuAIWQWwSGG1O010WShQG2ELsvNdg5/4HVdCGNl5mmoom6JOd72FOZyQlHDFfeQUQRn9HOeCq/c51rK99SQ== bartek@IHM"
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICTR292kx/2CNuWYIsZ6gykQ036aBGrmheIuZa6S1D2x implr@thonk"
   ];
-
 }
diff --git a/bgpwtf/machines/modules/routing.nix b/bgpwtf/machines/modules/routing.nix
new file mode 100644
index 0000000..e87ab9d
--- /dev/null
+++ b/bgpwtf/machines/modules/routing.nix
@@ -0,0 +1,440 @@
+# Declarative routing configuration. Usees BIRD2 underneath.
+#
+# The mapping from declarative configuration to BIRD is quite straightforward,
+# however, we take a few liberties:
+#  - we introduce an 'originate' protocol for originating prefixes (using the
+#    static protocol).
+#  - routing tables in the configuration are referred to by a common name for
+#    IPv4 and IPv4 - while in BIRD, two tables are created (suffixed by '4' and
+#    '6', following the two default 'master4' and 'master6' tables).
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hscloud.routing;
+
+  originateType = af: let
+    v4 = af == "ipv4";
+    v6 = af == "ipv6";
+    pretty = if v4 then "IPv4" else "IPv6";
+  in with types; mkOption {
+    type = attrsOf (submodule {
+      options = {
+        table = mkOption {
+          type = nullOr str;
+          description = "BIRD table to which session should be connected.";
+        };
+        address = mkOption {
+          type = str;
+          description = "Address part of prefix to announce.";
+        };
+        prefixLength = mkOption {
+          type = int;
+          description = "Prefix length to announce.";
+        };
+      };
+    });
+    default = {};
+    description = "${pretty} prefixes to unconditionally inject into a table.";
+  };
+
+  originateRender = af: n: v: let
+    name = "static_originate_${af}_${n}";
+    ip = if af == "ipv4" then "4" else "6";
+  in ''
+    protocol static ${name} {
+      ${af} {
+        table ${v.table}${ip};
+        import all;
+        export none;
+      };
+
+      route ${v.address}/${toString v.prefixLength} blackhole;
+    }
+  '';
+
+  ospfType = af: let
+    v4 = af == "ipv4";
+    v6 = af == "ipv6";
+    pretty = if v4 then "IPv4" else "IPv6";
+    ospf = if v4 then "OSPFv2" else "OSPFv3";
+  in with types; mkOption {
+    type = attrsOf (submodule {
+      options = {
+        table = mkOption {
+          type = nullOr str;
+          description = "BIRD table to which session should be connected.";
+        };
+        filterIn = mkOption {
+          type = str;
+          default = "accept;";
+          description = "BIRD filter definition for received routes.";
+        };
+        filterOut = mkOption {
+          type = str;
+          default = "accept;";
+          description = "BIRD filter definition for sent routes.";
+        };
+        area = mkOption {
+          type = attrsOf (submodule {
+            options = {
+              interfaces = mkOption {
+                type = attrsOf (submodule {
+                  options = {
+                    cost = mkOption {
+                      type = int;
+                      default = 10; # 1Gbps
+                      description = "Interface cost (10e9/iface_speed_in_bps).";
+                    };
+                    type = mkOption {
+                      type = enum ["bcast" "nbma" "ptp" "ptmp"];
+                      description = "Interface type (dictates BIRD behaviour).";
+                    };
+                    stub = mkOption {
+                      type = bool;
+                      default = false;
+                      description = "Interface is stub (do not HELLO).";
+                    };
+                  };
+                });
+                description = "Interface configuration";
+              };
+            };
+          });
+          description = "Area configuration";
+        };
+      };
+    });
+    default = {};
+    description = "${ospf} configuration";
+  };
+
+  ospfRender = af: n: v: let
+    v4 = af == "ipv4";
+    v6 = af == "ipv6";
+    ip = if v4 then "4" else "6";
+    name = "ospf_${af}_${n}";
+
+    interfaces = mapAttrsToList (iface: ifaceConfig: ''
+      interface "${iface}" {
+        type ${ifaceConfig.type};
+        cost ${toString ifaceConfig.cost};
+        ${if ifaceConfig.stub then "stub yes;" else ""}
+      };
+    '');
+    areas = mapAttrsToList (area: areaConfig: ''
+      area ${area} {
+        ${concatStringsSep "\n" (interfaces areaConfig.interfaces)}
+      };
+    '') v.area;
+  in ''
+    filter ${name}_in {
+      ${v.filterIn}
+    };
+    filter ${name}_out {
+      ${v.filterOut}
+    };
+    protocol ospf ${if v4 then "v2" else "v3"} ${name} {
+      ${af} {
+        table ${v.table}${ip};
+        import filter ${name}_in;
+        export filter ${name}_out;
+      };
+      ${concatStringsSep "\n" areas}
+    }
+  '';
+
+  pipeType = af: with types; mkOption {
+    type = attrsOf (submodule {
+      options = {
+        table = mkOption {
+          type = nullOr str;
+          description = "BIRD table to which session should be connected.";
+        };
+        peerTable = mkOption {
+          type = nullOr str;
+          description = "BIRD 'remote' table to which session should be connected.";
+        };
+        filterIn = mkOption {
+          type = str;
+          default = "accept";
+          description = "BIRD filter definition for routes received from peerTable";
+        };
+        filterOut = mkOption {
+          type = str;
+          default = "reject;";
+          description = "BIRD filter definition for routes sent to peerTable";
+        };
+      };
+    });
+    default = {};
+    description = "${pretty} prefixes to pipe from one table to another.";
+  };
+
+  pipeRender = af: n: v: let
+    name = "pipe_${af}_${n}";
+    v4 = af == "ipv4";
+    v6 = af == "ipv6";
+    ip = if v4 then "4" else "6";
+  in ''
+    filter ${name}_in {
+      ${v.filterIn}
+    };
+    filter ${name}_out {
+      ${v.filterOut}
+    };
+    protocol pipe ${name} {
+      table ${v.table}${ip};
+      peer table ${v.peerTable}${ip};
+      import filter ${name}_in;
+      export filter ${name}_out;
+    }
+  '';
+
+  bgpSessionsType = af: let
+    v4 = af == "ipv4";
+    v6 = af == "ipv6";
+    pretty = if v4 then "IPv4" else "IPv6";
+  in with types; mkOption {
+    type = attrsOf (submodule {
+      options = {
+        description = mkOption {
+          type = str;
+          description = "Session description (for BIRD).";
+        };
+        table = mkOption {
+          type = nullOr str;
+          description = "BIRD table to which session should be connected.";
+        };
+        local = mkOption {
+          type = str;
+          description = "${pretty} address of this router.";
+        };
+        asn = mkOption {
+          type = int;
+          description = "ASN of local router - will default to hscloud.routing.asn.";
+          default = cfg.asn;
+        };
+        prepend = mkOption {
+          type = int;
+          default = 0;
+          description = "How many times to prepend this router's ASN on the link.";
+        };
+        pref = mkOption {
+          type = int;
+          default = 100;
+          description = "Preference (BGP local_pref) for routes from this session.";
+        };
+        direct = mkOption {
+          type = nullOr bool;
+          default = null;
+        };
+        filterIn = mkOption {
+          type = str;
+          default = "accept;";
+          description = "BIRD filter definition for received routes.";
+        };
+        filterOut = mkOption {
+          type = str;
+          default = "accept;";
+          description = "BIRD filter definition for sent routes.";
+        };
+        neighbors = mkOption {
+          type = listOf (submodule {
+            options = {
+              address = mkOption {
+                type = str;
+                description = "${pretty} address of neighbor.";
+              };
+              asn = mkOption {
+                type = int;
+                description = "ASN of neighbor.";
+              };
+              password = mkOption {
+                type = nullOr str;
+                default = null;
+                description = "BGP TCP MD5 secret.";
+              };
+            };
+          });
+          description = "BGP Neighbor configuration";
+        };
+      };
+    });
+    default = {};
+    description = "BGP Sesions for ${pretty}";
+  };
+
+  bgpSessionRender = af: n: v: let
+    name = "bgp_${af}_${n}";
+    ip = if af == "ipv4" then "4" else "6";
+    filters = ''
+      filter ${name}_in {
+        if bgp_path.len > 64 then reject;
+        bgp_local_pref = ${toString v.pref};
+        ${v.filterIn}
+      }
+
+      filter ${name}_out {
+        ${if v.prepend > 0 then
+          (concatStringsSep "\n" 
+            (map (_: "bgp_path.prepend(${toString v.asn});") (range 0 (v.prepend - 1)))
+          )
+        else ""}
+        ${v.filterOut}
+      }
+    '';
+    peer = ix: peer: ''
+      protocol bgp ${name}_${toString ix} {
+        description "${v.description}";
+
+        ${af} {
+            table ${v.table}${ip};
+            import filter ${name}_in;
+            export filter ${name}_out;
+        };
+
+        local ${v.local} as ${toString v.asn};
+        neighbor ${peer.address} as ${toString peer.asn};
+        ${if peer.password != null then "password \"${peer.password}\";" else ""}
+        ${if v.direct == true then "direct;" else ""}
+      }
+    '';
+  in "${filters}\n${concatStringsSep "\n" (imap1 peer v.neighbors)}";
+
+  tablesFromProtoAF =
+    af: p: filter (el: el != null) (
+      mapAttrsToList (_: v: "${af} table ${v.table}${if af == "ipv4" then "4" else "6"};") p);
+  tablesFromProto = p: (tablesFromProtoAF "ipv4" p.v4) ++ (tablesFromProtoAF "ipv6" p.v6);
+  tables =
+    unique (
+      (tablesFromProto cfg.bgpSessions) ++
+      (tablesFromProto cfg.originate) ++
+      (tablesFromProto cfg.pipe) ++
+      (tablesFromProto cfg.ospf)
+      # TODO(q3k): also slurp in peer tables from pipes.
+    );
+  tablesRender = ''
+    ${concatStringsSep "\n" tables}
+  '';
+  tablesProgram = mapAttrsToList (n: _: n) (filterAttrs (n: v: v.program == true) cfg.tables);
+  tableProgram =
+    if (length tablesProgram) != 1 then
+      (abort "exactly one table must be set to be programmed")
+    else 
+      (head tablesProgram);
+
+in {
+  options.hscloud.routing = {
+    enable = mkEnableOption "declarative routing";
+    routerID = mkOption {
+      type = types.str;
+      description = ''
+        Default Router ID for dynamic routing protocols, eg. IPv4 address from
+        loopback interface.
+      '';
+    };
+    asn = mkOption {
+      type = types.int;
+      description = "Default ASN for BGP.";
+    };
+    extra = mkOption {
+      type = types.lines;
+      description = "Extra configuration lines.";
+    };
+    bgpSessions = {
+      v4 = bgpSessionsType "ipv4";
+      v6 = bgpSessionsType "ipv6";
+    };
+    originate = {
+      v4 = originateType "ipv4";
+      v6 = originateType "ipv6";
+    };
+    pipe = {
+      v4 = pipeType "ipv4";
+      v6 = pipeType "ipv6";
+    };
+    ospf = {
+      v4 = ospfType "ipv4";
+      v6 = ospfType "ipv6";
+    };
+    tables = mkOption {
+      type = types.attrsOf (types.submodule {
+        options = {
+          program = mkOption {
+            type = types.bool;
+            default = false;
+            description = "This is the primary table programmed in to the kernel.";
+          };
+          programSourceV4 = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = "If set, programmed routes will have source set to this address.";
+          };
+          programSourceV6 = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = "If set, programmed routes will have source set to this address.";
+          };
+        };
+      });
+      description = "Routing table configuration.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.bird2.enable = true;
+    services.bird2.config = ''
+      log syslog all;
+      debug protocols { states, interfaces, events }
+
+      router id ${cfg.routerID};
+
+      ${cfg.extra}
+
+      ${tablesRender}
+
+      protocol device {
+        scan time 10;
+      };
+
+      protocol kernel kernel_v4 {
+        scan time 60;
+        ipv4 {
+          table ${tableProgram}4;
+          import none;
+          export filter {
+            ${let src = cfg.tables."${tableProgram}".programSourceV4; in if src != null then ''
+              krt_prefsrc = ${src};
+            '' else ""}
+            accept;
+          };
+        };
+      }
+      protocol kernel kernel_v6 {
+        scan time 60;
+        ipv6 {
+          table ${tableProgram}6;
+          import none;
+          export filter {
+            ${let src = cfg.tables."${tableProgram}".programSourceV6; in if src != null then ''
+              krt_prefsrc = ${src};
+            '' else ""}
+            accept;
+          };
+        };
+      };
+
+      ${concatStringsSep "\n" (mapAttrsToList (bgpSessionRender "ipv4") cfg.bgpSessions.v4)}
+      ${concatStringsSep "\n" (mapAttrsToList (bgpSessionRender "ipv6") cfg.bgpSessions.v6)}
+      ${concatStringsSep "\n" (mapAttrsToList (originateRender "ipv4") cfg.originate.v4)}
+      ${concatStringsSep "\n" (mapAttrsToList (originateRender "ipv6") cfg.originate.v6)}
+      ${concatStringsSep "\n" (mapAttrsToList (pipeRender "ipv4") cfg.pipe.v4)}
+      ${concatStringsSep "\n" (mapAttrsToList (pipeRender "ipv6") cfg.pipe.v6)}
+      ${concatStringsSep "\n" (mapAttrsToList (ospfRender "ipv4") cfg.ospf.v4)}
+      ${concatStringsSep "\n" (mapAttrsToList (ospfRender "ipv6") cfg.ospf.v6)}
+
+    '';
+  };
+}
diff --git a/bgpwtf/machines/modules/rsh-unbound.nix b/bgpwtf/machines/modules/rsh-unbound.nix
new file mode 100644
index 0000000..20442fc
--- /dev/null
+++ b/bgpwtf/machines/modules/rsh-unbound.nix
@@ -0,0 +1,70 @@
+# Run service that spits out an unbound-compatible blocklist of websites,
+# as mandated by polish telecommunications law.
+#
+# https://sip.lex.pl/akty-prawne/dzu-dziennik-ustaw/gry-hazardowe-17581037/art-15-f
+# Dz.U.2019.847 t.j.
+# Art. 15f. [Rejestr domen służących do oferowania gier hazardowych niezgodnie z ustawą]
+# 5.  Przedsiębiorca telekomunikacyjny świadczący usługi dostępu do sieci
+#     Internet jest obowiÄ…zany do:
+#  1) nieodpłatnego uniemożliwienia dostępu do stron internetowych
+#     wykorzystujÄ…cych nazwy domen internetowych wpisanych do Rejestru
+#     poprzez ich usuniÄ™cie z systemów teleinformatycznych przedsiÄ™biorców
+#     telekomunikacyjnych, służących do zamiany nazw domen internetowych na
+#     adresy IP, nie później niż w ciÄ…gu 48 godzin od dokonania wpisu do
+#     Rejestru;
+#  2) nieodpłatnego przekierowania połączeń odwołujących się do nazw domen
+#     internetowych wpisanych do Rejestru do strony internetowej prowadzonej
+#     przez ministra wÅ‚aÅ›ciwego do spraw finansów publicznych, zawierajÄ…cej
+#     komunikat, skierowany do odbiorców usÅ‚ugi dostÄ™pu do Internetu
+#     obejmujÄ…cy w szczególnoÅ›ci informacje o lokalizacji Rejestru, wpisaniu
+#     szukanej nazwy domeny internetowej do tego Rejestru, listÄ™ podmiotów
+#     legalnie oferujÄ…cych gry hazardowe na terytorium Rzeczypospolitej
+#     Polskiej, a także powiadomienie o grożącej odpowiedzialności
+#     karno-skarbowej uczestnika gier urzÄ…dzanych wbrew przepisom ustawy;
+#  3) nieodpłatnego umożliwienia dostępu do stron internetowych
+#     wykorzystujÄ…cych nazwy domen wykreÅ›lonych z Rejestru, nie później niż w
+#     ciągu 48 godzin od wykreślenia nazwy domeny internetowej z Rejestru.
+
+{ config, pkgs, lib, ...  }:
+
+with lib;
+
+let
+  rshUnbound = pkgs.buildGoModule {
+    pname = "rsh-unbound";
+    version = "20200926";
+    src = pkgs.fetchFromGitHub {
+      owner = "q3k";
+      repo = "rsh-unbound";
+      rev = "3d98c754adadddfae59387d033aef531f47dee5d";
+      sha256 = "1ia33893m1dknw36vss97limlb1d28z5nkrkw6b4mp1igdgqsfcz";
+    };
+
+    vendorSha256 = "1w94g2dwhf47jmds95frb26ypjmis5zhyy85rmd124v0nz3axzhf";
+  };
+
+  cfg = config.hscloud.rsh;
+
+in {
+  options.hscloud.rsh = with types; {
+    enable = mkOption {
+      type = bool;
+      default = false;
+      description = "Enable the RSH-Unboudn service.";
+    };
+    out = mkOption {
+      type = str;
+      description = "Output file for generated unbound config.";
+    };
+  };
+
+  config.systemd.services.rsh = mkIf cfg.enable {
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      Type = "simple";
+      ExecStart = "${rshUnbound}/bin/rsh-unbound -output ${cfg.out} -register_endpoint https://hazard.mf.gov.pl/api/Register";
+      Restart = "always";
+      RestartSec = "60";
+    };
+  };
+}
diff --git a/bgpwtf/machines/secrets/cipher/edge01.waw.bgp.wtf-private.nix b/bgpwtf/machines/secrets/cipher/edge01.waw.bgp.wtf-private.nix
new file mode 100644
index 0000000..9fe6c17
--- /dev/null
+++ b/bgpwtf/machines/secrets/cipher/edge01.waw.bgp.wtf-private.nix
@@ -0,0 +1,49 @@
+-----BEGIN PGP MESSAGE-----
+
+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
+MZ/1vrhwOkOdDIh5spJJKXwYpBUivr/XLaQ=
+=b7js
+-----END PGP MESSAGE-----
diff --git a/bgpwtf/machines/secrets/cipher/passwords.nix b/bgpwtf/machines/secrets/cipher/passwords.nix
new file mode 100644
index 0000000..bf93736
--- /dev/null
+++ b/bgpwtf/machines/secrets/cipher/passwords.nix
@@ -0,0 +1,40 @@
+-----BEGIN PGP MESSAGE-----
+
+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
+=d5Gq
+-----END PGP MESSAGE-----
diff --git a/bgpwtf/machines/secrets/plain/.gitignore b/bgpwtf/machines/secrets/plain/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/bgpwtf/machines/secrets/plain/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/bgpwtf/machines/tests/edge01-waw.nix b/bgpwtf/machines/tests/edge01-waw.nix
new file mode 100644
index 0000000..e0298d2
--- /dev/null
+++ b/bgpwtf/machines/tests/edge01-waw.nix
@@ -0,0 +1,288 @@
+# Smoke test edge01.waw in a multi-VM NixOS test.
+#
+# This brings up three VMs:
+# - dut/edge01
+# - bgpspeaker, which simulates bgp upstreams
+# - customs, which simulates customs.hackerspace.pl.
+#
+# We use EoIP to build up virtual ethernet links between the machines, and
+# to run VLANs on that. We don't just use plain 'vlans' from NixOS tests as
+# we actually want to run 802.1q ourselves from the edge01 config.
+#
+# Everything else is pretty much straightforward. Bring up everything, ping
+# stuff. We don't really test much else than internet routing.
+#
+# To run this:
+#  nix-build -A bgpwtf.machines.tests.edge01-waw
+#
+# To debug this:
+#  nix-build -A bgpwtf.machines.tests.edge01-waw.driver && result/bin/nixos-test-driver
+#  >>> start_all()
+
+{ hscloud, pkgsSrc, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+mkBGPSpeaker = let
+in { config, pkgs, ... }: {
+  networking.hostName = "bgpspeaker";
+  virtualisation.memorySize = 1024;
+  virtualisation.vlans = [ 1 ];
+  imports = [
+    ../modules/eoip.nix
+  ];
+
+  hscloud.eoip.interfaces."nnet" = {
+    parent = "eth1";
+    localV4 = "192.168.1.3";
+    remoteV4 = "192.168.1.2";
+    id = 100;
+  };
+  networking.interfaces."nnet" = {
+    virtual = true;
+    virtualType = "tap";
+  };
+  networking.vlans = {
+    "vl-globalmix" = { interface = "nnet"; id = 466; };
+  };
+  networking.interfaces."vl-globalmix" = {
+    ipv4.addresses = [{ address = "185.235.70.44"; prefixLength = 31; }];
+    ipv6.addresses = [{ address = "2001:67c:778:fd40::b9eb:462c"; prefixLength = 127; }];
+  };
+
+  services.bird2 = {
+    enable = true;
+    config = ''
+      log syslog all;
+      debug protocols { states, interfaces, events }
+      router id 185.235.70.44;
+
+      protocol device {
+        scan time 10;
+      };
+      protocol kernel kernel_v4 {
+        ipv4 {
+          import none;
+          export all;
+        };
+      }
+      protocol kernel kernel_v6 {
+        ipv6 {
+          import none;
+          export all;
+        };
+      }
+      ipv4 table globalmix4;
+      ipv6 table globalmix6;
+
+      protocol pipe pipe_globalmix4 {
+        table master4;
+        peer table globalmix4;
+        import all;
+        export none;
+      };
+      protocol pipe pipe_globalmix6 {
+        table master6;
+        peer table globalmix6;
+        import all;
+        export none;
+      };
+
+      protocol static static_globalmix_originate_v4 {
+        ipv4 {
+          table globalmix4;
+          import all;
+        };
+        route 8.8.8.0/24 blackhole;
+      }
+      protocol static static_globalmix_originate_v6 {
+        ipv6 {
+          table globalmix6;
+          import all;
+        };
+        route 2a00:1450:4016::/48 blackhole;
+      }
+      protocol bgp bgp_globalmix_v4 {
+        ipv4 {
+          table globalmix4;
+          export all;
+          import all;
+        };
+        local 185.235.70.44 as 62081;
+        neighbor 185.235.70.45 as 204880;
+      };
+      protocol bgp bgp_globalmix_v6 {
+        ipv6 {
+          table globalmix6;
+          export all;
+          import all;
+        };
+        local 2001:67c:778:fd40::b9eb:462c as 62081;
+        neighbor 2001:67c:778:fd40::b9eb:462d as 204880;
+      };
+    '';
+  };
+  networking.firewall.enable = false;
+  networking.useDHCP = false;
+  networking.interfaces.lo.ipv4.addresses = [ { address = "8.8.8.1"; prefixLength = 32; } ];
+  networking.interfaces.lo.ipv6.addresses = [ { address = "2a00:1450:4016:801::200e"; prefixLength = 128; } ];
+  environment.systemPackages = with pkgs; [
+    tcpdump htop dstat file
+  ];
+
+};
+
+
+test = import "${pkgsSrc}/nixos/tests/make-test-python.nix" ({ pkgs, libs, ... }: {
+  name = "test-edge01-waw-e2e";
+
+  nodes = {
+    dut = { config, pkgs, ... }: {
+      imports = [
+        ../edge01.waw.bgp.wtf.nix
+        ../modules/eoip.nix
+      ];
+      virtualisation.memorySize = 1024;
+      virtualisation.vlans = [
+        1 2
+      ];
+
+      hscloud.eoip.interfaces = {
+        "e1-nnet" = { parent = "eth1"; localV4 = "192.168.1.2"; remoteV4 = "192.168.1.3"; id = 100; };
+        "e2-customs" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.1"; id = 200; };
+        "e3-mgmt" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.111"; id = 300; }; # not connected
+        "e4-oob" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.112"; id = 400; }; # not connected
+        "e7-dcsw" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.113"; id = 500; }; # not connected
+      };
+      networking.interfaces = {
+        "e1-nnet" = { virtual = true; virtualType = "tap"; };
+        "e2-customs" = { virtual = true; virtualType = "tap"; };
+        "e3-mgmt" = { virtual = true; virtualType = "tap"; };
+        "e4-oob" = { virtual = true; virtualType = "tap"; };
+        "e7-dcsw" = { virtual = true; virtualType = "tap"; };
+      };
+    };
+
+    speaker = mkBGPSpeaker;
+
+    customs = { config, pkgs, ... }: {
+      imports = [
+        ../modules/eoip.nix
+      ];
+      environment.systemPackages = with pkgs; [
+        tcpdump htop dstat file dhcpcd
+      ];
+      virtualisation.memorySize = 1024;
+      virtualisation.vlans = [
+        2
+      ];
+      networking.firewall.enable = false;
+      networking.useDHCP = false;
+      networking.defaultGateway = "185.236.240.4";
+      networking.defaultGateway6 = "2a0d:eb00:2137:1::2";
+      networking.interfaces."edge" = {
+        virtual = true;
+        virtualType = "tap";
+        ipv4.addresses = [{ address = "185.236.240.5"; prefixLength = 31; }];
+        ipv6.addresses = [{ address = "2a0d:eb00:2137:1::3"; prefixLength = 127; }];
+      };
+      hscloud.eoip.interfaces."edge" = {
+        parent = "eth2";
+        localV4 = "192.168.2.1";
+        remoteV4 = "192.168.2.2";
+        id = 200;
+      };
+      networking.bridges."lan".interfaces = [];
+      networking.interfaces."lan" = {
+        ipv4.addresses = [{ address = "10.8.1.2"; prefixLength = 23; }];
+        ipv6.addresses = [{ address = "2a0d:eb00:4242::1"; prefixLength = 64; }];
+      };
+      services.bird2 = {
+        enable = true;
+        config = ''
+          log syslog all;
+          debug protocols { states, interfaces, events }
+          router id 185.236.240.5;
+
+          protocol device {
+            scan time 10;
+          };
+          protocol kernel kernel_v4 {
+            ipv4 {
+              import none;
+              export all;
+            };
+          }
+          protocol kernel kernel_v6 {
+            ipv6 {
+              import none;
+              export all;
+            };
+          }
+
+          protocol ospf v3 ospf_hswaw {
+            ipv6 {
+              import all;
+              export all;
+            };
+            area 0.0.0.0 {
+               interface "edge" {
+                 cost 10;
+                 type bcast;
+               };
+               interface "lan" {
+                 cost 10;
+                 stub yes;
+                 type bcast;
+                 check link no;
+               };
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    edge01.wait_for_unit("bird2.service")
+    # Wait for BGP to settle.
+    edge01.wait_until_succeeds("ping 185.235.70.44 -c 1 -w 2")
+    edge01.wait_until_succeeds("birdc show route for 8.8.8.1 table all | grep via")
+    edge01.wait_until_succeeds(
+        "birdc show route for 2a00:1450:4016:801::200e table all | grep via"
+    )
+    edge01.succeed("ping 8.8.8.1 -c 1 -w 2")
+
+    # ping from customs to globalmix must succeed.
+    customs.succeed("ping 8.8.8.1 -c 1 -w 2")
+    customs.succeed("ping 2a00:1450:4016:801::200e -c 1 -w 2")
+
+    # edge01 must announce exactly one v4 prefix.
+    bgpspeaker.succeed("birdc show route protocol bgp_globalmix_v4 | grep unicast")
+    bgpspeaker.fail(
+        "birdc show route protocol bgp_globalmix_v4 | grep unicast | grep -v 185.236.240.0/23"
+    )
+
+    # edge01 must announce exactly one v6 prefix.
+    bgpspeaker.succeed("birdc show route protocol bgp_globalmix_v6 | grep unicast")
+    bgpspeaker.fail(
+        "birdc show route protocol bgp_globalmix_v6 | grep unicast | grep -v 2a0d:eb00::/32"
+    )
+
+    # customer networks must be reachable from globalmix
+    bgpspeaker.succeed("ping 185.236.240.10 -c 1 -w 2")
+    bgpspeaker.succeed("ping 2a0d:eb00:8000::1 -c 1 -w 2")
+    bgpspeaker.succeed("ping 185.236.240.12 -c 1 -w 2")
+    bgpspeaker.succeed("ping 185.236.240.105 -c 1 -w 2")
+    bgpspeaker.succeed("ping 2a0d:eb00:8003::1 -c 1 -w 2")
+
+    # dhcp agent must be reachable
+    customs.succeed("ping 185.236.240.18 -c 1 -w 2")
+  '';
+});
+
+in test { inherit pkgs; inherit (pkgs) libs; }
diff --git a/bgpwtf/speedtest/BUILD.bazel b/bgpwtf/speedtest/BUILD.bazel
index cc431d0..7b5af44 100644
--- a/bgpwtf/speedtest/BUILD.bazel
+++ b/bgpwtf/speedtest/BUILD.bazel
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_docker//container:container.bzl", "container_image")
+load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")
 load("@io_bazel_rules_go//go:def.bzl", "go_library")
 load("@io_bazel_rules_go//extras:embed_data.bzl", "go_embed_data")
 
@@ -24,19 +24,11 @@
     entrypoint = ["/hscloud/backend"],
 )
 
-genrule(
-    name = "push_latest",
-    srcs = [":latest"],
-    outs = ["version.sh"],
-    executable = True,
-    cmd = """
-        local=bazel/bgpwtf/speedtest:latest
-        tag=$$(date +%s)
-        remote=registry.k0.hswaw.net/bgpwtf/speedtest:$$tag
-
-        docker tag $$local $$remote
-        docker push $$remote
-        echo -ne "#!/bin/sh\necho Pushed $$remote\n" > $(OUTS)
-    """,
+container_push(
+    name = "push",
+    image = ":latest",
+    format = "Docker",
+    registry = "registry.k0.hswaw.net",
+    repository = "q3k/speedetst",
+    tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}",
 )
-
diff --git a/bgpwtf/speedtest/backend/main.go b/bgpwtf/speedtest/backend/main.go
index 7d4aece..3473352 100644
--- a/bgpwtf/speedtest/backend/main.go
+++ b/bgpwtf/speedtest/backend/main.go
@@ -134,9 +134,11 @@
 		w.Write(static.Data["bgpwtf/speedtest/index.html"])
 	})
 	http.HandleFunc("/speedtest.js", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "text/javascript")
 		w.Write(static.Data["bgpwtf/speedtest/speedtest.js"])
 	})
 	http.HandleFunc("/speedtest_worker.js", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "text/javascript")
 		w.Write(static.Data["bgpwtf/speedtest/speedtest_worker.js"])
 	})
 
diff --git a/bgpwtf/speedtest/kube/prod.jsonnet b/bgpwtf/speedtest/kube/prod.jsonnet
index fc6e083..9187f54 100644
--- a/bgpwtf/speedtest/kube/prod.jsonnet
+++ b/bgpwtf/speedtest/kube/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import '../../../../kube/kube.libsonnet';
+local kube = import '../../../kube/kube.libsonnet';
 
 {
     local speedtest = self,
@@ -8,8 +8,7 @@
         appName: "speedtest",
         domain: "speedtest.hackerspace.pl",
 
-        tag: "1563032542",
-        image: "registry.k0.hswaw.net/bgpwtf/speedtest:" + cfg.tag,
+        image: "registry.k0.hswaw.net/q3k/speedetst:1601997887-f9d9c752ad8403c2b284bfeab23111b25f7e2214",
 
         resources: {
             requests: {
diff --git a/bzl/workspace-status.sh b/bzl/workspace-status.sh
index 5450017..c039372 100755
--- a/bzl/workspace-status.sh
+++ b/bzl/workspace-status.sh
@@ -15,6 +15,7 @@
 }
 
 echo STABLE_BUILD_GERRIT-OAUTH-PROVIDER_LABEL $(rev .)
+echo STABLE_BUILD_OWNERS_LABEL $(rev .)
 echo STABLE_GIT_COMMIT $(git rev-parse HEAD)
 echo STABLE_GIT_VERSION $(rev .)
 echo STABLE_BUILDER $(id -un)@$(hostname -f):$(pwd)
diff --git a/ci_presubmit.sh b/ci_presubmit.sh
new file mode 100755
index 0000000..5abaf10
--- /dev/null
+++ b/ci_presubmit.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+# This is a presubmit (on-merge) CI script.
+# Currently no CI runs it - so be a good cyborg, and run it before submitting
+# changes to hscloud.
+
+set -e -o pipefail
+
+if [ -z "$hscloud_root" ]; then
+    echo 2>&1 "Please first source env.sh"
+    exit 1
+fi
+
+cd $hscloud_root
+
+# Test critical tools.
+tools/install.sh
+kubectl version --client=true
+kubecfg version
+prodaccess --help 2>/dev/null
+bazel run //cluster/clustercfg smoketest
diff --git a/cluster/README b/cluster/README
deleted file mode 100644
index 0120859..0000000
--- a/cluster/README
+++ /dev/null
@@ -1,91 +0,0 @@
-HSCloud Clusters
-================
-
-Current cluster: `k0.hswaw.net`
-
-Accessing via kubectl
----------------------
-
-    prodaccess # get a short-lived certificate for your use via SSO
-               # if youre local username is not the same as your HSWAW SSO
-               # username, pass `-username foo`
-    kubectl version
-    kubectl top nodes
-
-Every user gets a `personal-$username` namespace. Feel free to use it for your own purposes, but watch out for resource usage!
-
-    kubectl run -n personal-$username run --image=alpine:latest -it foo
-
-To proceed further you should be somewhat familiar with Kubernetes. Otherwise the rest of terminology might not make sense. We recommend going through the original Kubernetes tutorials.
-
-Persistent Storage (waw2)
--------------------------
-
-HDDs on bc01n0{1-3}. 3TB total capacity. Don't use this as this pool should go away soon (the disks are slow, the network is slow and the RAID controllers lie). Use ceph-waw3 instead.
-
-The following storage classes use this cluster:
-
- - `waw-hdd-paranoid-1` - 3 replicas
- - `waw-hdd-redundant-1` - erasure coded 2.1
- - `waw-hdd-yolo-1` - unreplicated (you _will_ lose your data)
- - `waw-hdd-redundant-1-object` - erasure coded 2.1 object store
-
-Rados Gateway (S3) is available at https://object.ceph-waw2.hswaw.net/. To create a user, ask an admin.
-
-PersistentVolumes currently bound to PersistentVolumeClaims get automatically backed up (hourly for the next 48 hours, then once every 4 weeks, then once every month for a year).
-
-Persistent Storage (waw3)
--------------------------
-
-HDDs on dcr01s2{2,4}. 40TB total capacity for now. Use this.
-
-The following storage classes use this cluster:
-
- - `waw-hdd-yolo-3` - 1 replica
- - `waw-hdd-redundant-3` - 2 replicas
- - `waw-hdd-redundant-3-object` - 2 replicas, object store
-
-Rados Gateway (S3) is available at https://object.ceph-waw3.hswaw.net/. To create a user, ask an admin.
-
-PersistentVolumes currently bound to PVCs get automatically backed up (hourly for the next 48 hours, then once every 4 weeks, then once every month for a year).
-
-Administration
-==============
-
-Provisioning nodes
-------------------
-
- - bring up a new node with nixos, the configuration doesn't matter and will be nuked anyway
- - edit cluster/nix/defs-machines.nix
- - `bazel run //cluster/clustercfg nodestrap bc01nXX.hswaw.net`
-
-Ceph - Debugging
------------------
-
-We run Ceph via Rook. The Rook operator is running in the `ceph-rook-system` namespace. To debug Ceph issues, start by looking at its logs.
-
-A dashboard is available at https://ceph-waw2.hswaw.net/, to get the admin password run:
-
-    kubectl -n ceph-waw2 get secret rook-ceph-dashboard-password -o yaml | grep "password:" | awk '{print $2}' | base64 --decode ; echo
-
-
-Ceph - Backups
---------------
-
-Kubernetes PVs backed in Ceph RBDs get backed up using Benji. An hourly cronjob runs in every Ceph cluster. You can also manually trigger a run by doing:
-
-    kubectl -n ceph-waw2 create job --from=cronjob/ceph-waw2-benji ceph-waw2-benji-manual-$(date +%s)
-
-Ceph ObjectStorage pools (RADOSGW) are _not_ backed up yet!
-
-Ceph - Object Storage
----------------------
-
-To create an object store user consult rook.io manual (https://rook.io/docs/rook/v0.9/ceph-object-store-user-crd.html)
-User authentication secret is generated in ceph cluster namespace (`ceph-waw2`),
-thus may need to be manually copied into application namespace. (see
-`app/registry/prod.jsonnet` comment)
-
-`tools/rook-s3cmd-config` can be used to generate test configuration file for s3cmd.
-Remember to append `:default-placement` to your region name (ie. `waw-hdd-redundant-1-object:default-placement`)
-
diff --git a/cluster/certs/ca-kube-prodvider.cert b/cluster/certs/ca-kube-prodvider.cert
index e5ec6d9..bd969f0 100644
--- a/cluster/certs/ca-kube-prodvider.cert
+++ b/cluster/certs/ca-kube-prodvider.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFQzCCBCugAwIBAgIUbcxmU7cMccTf/ERKgi0uDIKJRoEwDQYJKoZIhvcNAQEL
+MIIFQzCCBCugAwIBAgIUARcjVskxjZNWpolOFXdx0LRJPt8wDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA4
-MzAyMDI1MDBaFw0yMDA4MjkyMDI1MDBaMIGsMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDA5
+MDEyMTMwMDBaFw0yMTA5MDEyMTMwMDBaMIGsMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEbMBkGA1UEChMSV2Fyc2F3
 IEhhY2tlcnNwYWNlMSowKAYDVQQLEyFrdWJlcm5ldGVzIHByb2R2aWRlciBpbnRl
 cm1lZGlhdGUxLTArBgNVBAMTJGt1YmVybmV0ZXMgcHJvZHZpZGVyIGludGVybWVk
@@ -21,11 +21,11 @@
 DWl93HqdKeINlp9Q0HQ7nR+LUkeodWf7AgMBAAGjgYMwgYAwDgYDVR0PAQH/BAQD
 AgGmMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTAD
 AQH/MB0GA1UdDgQWBBRpjeqS08ZAgwwhQZnMEmrNN2PdszAfBgNVHSMEGDAWgBSY
-Ml0OTzMe+wnpiSQTFkJqgNGZ0DANBgkqhkiG9w0BAQsFAAOCAQEAiVxVjz4vuN0w
-9mw56taa8AxOF4Cl18LEuxVnw6ugxG5ahlhZOssnv/HdDwoHdlbLw5ER2RTK0hFT
-whH76BkJOUwAZ+YggpnOFf5hUIf9e3Pfu5MtdSBJQ0LHPRY3QPP/gHEsQR0muXVd
-AIyTQZPuJ2M98bWgaZX4yrJ31jLjcNPFM7RXiIi1ZgTr7LTRCALoFm1Tw/kM5TE7
-2qYjcaeJO1X3Zon5UXJogYa/3JreKQlBhGZgHHNAQobmVNmJTEvOuPw/31ZWDKVR
-Qrv04QYFUwCNGdI1Bin1rk9lbsrTiEP2x8W5cwGPaa1MR45xTrrEYBrplUJXiCBQ
-kwCwP+xLBQ==
+Ml0OTzMe+wnpiSQTFkJqgNGZ0DANBgkqhkiG9w0BAQsFAAOCAQEApx4PthrJy1Mu
+HXcTox9OgLYR7DSJq6lsIxnrqYSo08RtZ/yp/z0C52Ju65GpCYZZeNcD7TH0gTlr
+NbVXkowM/n+Uad8AmEn4CVvAuAKr1T9OZ9nvDPkx6hTzzxwpTyVLruW/PtFc+/7P
+qSAKrnDuIaSWfNC6Lv0FDGdG5MjqM30J74LiGfl69/rqjV/gBrDR9rtImwsGgGQV
+wPyy3hc3G2SCzaI3x3w7Epm7AwjjcW2YbkG5BBiHOG2Ul9Ps6ikCskwrWnu+VFsG
+zuefuvH+OIr58rlLtSgr+sTRTdiZcD/xUc0r+o2KJqU8AHUvcs1OLArEBBZDN4vs
+1PR0+NIOKg==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-bc01n01.hswaw.net.cert b/cluster/certs/etcd-bc01n01.hswaw.net.cert
index 26d4913..917ad22 100644
--- a/cluster/certs/etcd-bc01n01.hswaw.net.cert
+++ b/cluster/certs/etcd-bc01n01.hswaw.net.cert
@@ -1,29 +1,30 @@
 -----BEGIN CERTIFICATE-----
-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
+MIIFHDCCBASgAwIBAgIUBd/lkgCFa6VNTU862aFQGQd6i8gwDQYJKoZIhvcNAQEL
 BQAweDELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-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
+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
-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
+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
++lUKqLwE5y4V7f/FWIKfhg==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-bc01n02.hswaw.net.cert b/cluster/certs/etcd-bc01n02.hswaw.net.cert
index fbf7f60..5fb1c6d 100644
--- a/cluster/certs/etcd-bc01n02.hswaw.net.cert
+++ b/cluster/certs/etcd-bc01n02.hswaw.net.cert
@@ -1,29 +1,30 @@
 -----BEGIN CERTIFICATE-----
-MIIFADCCA+igAwIBAgIUEOvIsxRTRhb/gGkSusEaIvihjmYwDQYJKoZIhvcNAQEL
+MIIFHDCCBASgAwIBAgIUfKVgcr+CKsr3u9FsVXFRZRXv8B0wDQYJKoZIhvcNAQEL
 BQAweDELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0xOTA0MDYxODA0MDBaFw0y
-MDA0MDUxODA0MDBaMFsxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
+Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0yMDAzMjgxNjQ1MDBaFw0y
+MTAzMjgxNjQ1MDBaMHcxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
 ZTEPMA0GA1UEBxMGV2Fyc2F3MSUwIwYDVQQLExxub2RlIGV0Y2Qgc2VydmVyIGNl
-cnRpZmljYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwrMOjn2b
-ESHDHzHeO73kqaNs0P3AKBr1gZ9Tfpg62zkah5wvW3oQ44/WubwEeEZGi7im3Srq
-wmqCFLmKHGkpVx34qdlsTgKa8sCxIfrz8QuOXW8fr68l/hIkUXXE3cIwxOBWvRkz
-kH97bqWnrbSOvMuFquTQNppA7ynlWnSP1L2KquwaTn8hyguSJwCSiFAp8d7ShKHC
-Kb+msuFXYeNhPRnTflvLuNmg8IesTMTF9D0Q2k6aZ/jPGjUoJAQsftrnDIz8Wlzg
-QaraSEYW5FrVTWZtl0ZbRZmOk25wU0mZR7L6hpFwAePsgGiU9Si/fPVvANk8h0Ah
-E+m8k2mu79Gsw6TmvFU5KT8jNNZXagHLUjuqAwrFEz16ai/Z0UQfnn+NGwp4BioN
-iZujfndh3D7qPdCdxCOJS3qwHZ2G+z6JmF61N1hFwR5wrKxjsgZmQXQTm+L6mpT0
-JBss/uEcTiZe1KEHPUzEWv9gvhdzeXPAMKj15P4GQCMyE+3ozWM2WpZdLaGlaQ0f
-OJm4G+3hs3V6DXr44LpSDJWo8dVDUGBgjOYwJXgyaxyWFKTwlHBYZeAtd4jpKzqw
-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
+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
+C4FJ50A5mcfOEOvnKLA6Zw==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-bc01n03.hswaw.net.cert b/cluster/certs/etcd-bc01n03.hswaw.net.cert
index 254f443..e2575f9 100644
--- a/cluster/certs/etcd-bc01n03.hswaw.net.cert
+++ b/cluster/certs/etcd-bc01n03.hswaw.net.cert
@@ -1,29 +1,30 @@
 -----BEGIN CERTIFICATE-----
-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
+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
-Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0xOTA0MDYxODA1MDBaFw0y
-MDA0MDUxODA1MDBaMFsxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
+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
-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
+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
+mvS9hZ09yn7stK+5RxYgQA==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-calico.cert b/cluster/certs/etcd-calico.cert
index 62d8234..bbcd91d 100644
--- a/cluster/certs/etcd-calico.cert
+++ b/cluster/certs/etcd-calico.cert
@@ -1,29 +1,29 @@
 -----BEGIN CERTIFICATE-----
-MIIE9TCCA92gAwIBAgIUe09DNSzrB1J5weALRB+K2BeyvzMwDQYJKoZIhvcNAQEL
+MIIFBjCCA+6gAwIBAgIUZmjAlqCosP9W/6X/iIMLRrNb/bkwDQYJKoZIhvcNAQEL
 BQAweDELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0xOTA0MDYxNzU5MDBaFw0y
-MDA0MDUxNzU5MDBaMFsxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
+Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0yMDAzMjgxNTE1MDBaFw0y
+MTAzMjgxNTE1MDBaMGwxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
 ZTEPMA0GA1UEBxMGV2Fyc2F3MSUwIwYDVQQLExxyb290IGV0Y2QgY2xpZW50IGNl
-cnRpZmljYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvVUZSPAJ
-72SjzIbQ3n/Zq2MnXyDZSP0EvPghPGi5BX4UECR8o6qGxk6vvGVhVlmrf7ecPK8d
-AuMES0gcTwzei0cZkaFD2r5itpx+bE88emafTaJhKgnhYOoZ2gtT/bKISDocjnFz
-oZiLPHu108LJF4x2zIgnmnDvETll0zX3prVTkHQ7SPWpKDr/Pb5YYGPeyKjDWqJs
-9i5B8qcA4BEjZ1OGvfssa9gxaqfCYmLZQ2o4TLzJ/O21mGkP1+9vHwvA7tlJuwsp
-WTH5eYgW35qgDpAHjzQ67fPsj6E69n8eNC/9n5E2DS4GhBYzoiVuNYLl8UWUXeDU
-q3C+P85Z4gWtgarECOsgQw/4ClGQMlt0QqIwb+6XC5UestfoRiDXk6JaGr3l92k0
-g3nRz7IJU0aL+3YChnDxQec/LTVO25hLDUM0he0r5XcpjP/IXiuBwCWlxS6hJhxh
-A2QGkdJlPkeeQhggxoU5ZipM8YPE7tqyTK91+vSkYuZX6+a61u4ks7gNRB0hbwlp
-eEUTQvbDr34FuHaXK4z3z7PXfs1NZENH0BYbpADIDtJgYKvGmUjY8EIXH5gU05u7
-KItUbWlYalxt1pMPFfHWsXjLeglfJ+76D1zFTjsK62gXN2VtU7dqYe/Ix1aCRYFP
-qhyTt4iF/wK6WYn+Cm8fShC5TyA/48DmA/kCAwEAAaOBkzCBkDAOBgNVHQ8BAf8E
-BAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQC
-MAAwHQYDVR0OBBYEFGI6wkiqEwIBAnSmU57VJt/+3dj1MB8GA1UdIwQYMBaAFPFZ
-uGZNPsPnQu6Bo9RGfzlTdfkPMBEGA1UdEQQKMAiCBmNhbGljbzANBgkqhkiG9w0B
-AQsFAAOCAQEADnpq/+89AFr8NGJjphkiDW/pvef3c4+k//6S79NFkEnQaMEKwdte
-Xd/nNnyNYDJHU5AvX823Sv4KNqNSOraIwSSyWMeTrvI1plKkBRJ8+dv26VkHDd5P
-+yW75btsdp7TSleisfybGSttre/0AzSRgzTmnM3VkzAIHBOgXXmXi7BV6PeVd6jH
-aEIB/81S2u+j9mmFFMQ1Ur1sDtaLlOzEMH7RURXoXOgHAMYHUNe6tkBhy6/sXXLw
-lnZA6+1Qc/AIj/TcFcjApDL5zJ0ZIbmZscEU42D6hLcAGSHEPcCceXw1TtvmaPKp
-5mOZ0oukyNhXj+5llWNRGIdlnS+sBoGInA==
+cnRpZmljYXRlMQ8wDQYDVQQDEwZjYWxpY28wggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQC9VRlI8AnvZKPMhtDef9mrYydfINlI/QS8+CE8aLkFfhQQJHyj
+qobGTq+8ZWFWWat/t5w8rx0C4wRLSBxPDN6LRxmRoUPavmK2nH5sTzx6Zp9NomEq
+CeFg6hnaC1P9sohIOhyOcXOhmIs8e7XTwskXjHbMiCeacO8ROWXTNfemtVOQdDtI
+9akoOv89vlhgY97IqMNaomz2LkHypwDgESNnU4a9+yxr2DFqp8JiYtlDajhMvMn8
+7bWYaQ/X728fC8Du2Um7CylZMfl5iBbfmqAOkAePNDrt8+yPoTr2fx40L/2fkTYN
+LgaEFjOiJW41guXxRZRd4NSrcL4/zlniBa2BqsQI6yBDD/gKUZAyW3RCojBv7pcL
+lR6y1+hGINeToloaveX3aTSDedHPsglTRov7dgKGcPFB5z8tNU7bmEsNQzSF7Svl
+dymM/8heK4HAJaXFLqEmHGEDZAaR0mU+R55CGCDGhTlmKkzxg8Tu2rJMr3X69KRi
+5lfr5rrW7iSzuA1EHSFvCWl4RRNC9sOvfgW4dpcrjPfPs9d+zU1kQ0fQFhukAMgO
+0mBgq8aZSNjwQhcfmBTTm7soi1RtaVhqXG3Wkw8V8daxeMt6CV8n7voPXMVOOwrr
+aBc3ZW1Tt2ph78jHVoJFgU+qHJO3iIX/ArpZif4Kbx9KELlPID/jwOYD+QIDAQAB
+o4GTMIGQMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
+BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUYjrCSKoTAgECdKZTntUm3/7d
+2PUwHwYDVR0jBBgwFoAU8Vm4Zk0+w+dC7oGj1EZ/OVN1+Q8wEQYDVR0RBAowCIIG
+Y2FsaWNvMA0GCSqGSIb3DQEBCwUAA4IBAQCQV/w/TOPjnCJszWqLd5GeoEkPPE8o
+qevGTmTpm+/l/vlFERDsUB2kKnxb4mHBbVo2c2ux0aF53wKeIcp5/fdfH1LFjS67
+YY9hLce3zFmZMCEbkFGgpjQKpNy4zB72f4ksGRzbPienFLhghhY1dIv5Rdrhyz1O
+xhrUP9fgJHjYd33pFVfhyl8mIOon8yn+4AvGLrPATgp4dmkF+HM3EYtqd2LfeHVy
+Dc9PbjpmIGHz6IKpMKC4S6rlnnfzbk2PRULVcXHPtfX9ihXz+B892IORDK8jgSe8
+PdVe3ZGMo1EbSwQ9WwGZMrIqeiX1vfaEOhZgaw8GMuFM75OBK9yRopZ/
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-dcr01s22.hswaw.net.cert b/cluster/certs/etcd-dcr01s22.hswaw.net.cert
index 69930ec..5380b73 100644
--- a/cluster/certs/etcd-dcr01s22.hswaw.net.cert
+++ b/cluster/certs/etcd-dcr01s22.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFHjCCBAagAwIBAgIUHMTWqXaNMC+JIbfJV/cXWiAQJiwwDQYJKoZIhvcNAQEL
+MIIFHjCCBAagAwIBAgIUU8/CIgYDnyPPidO4Tssmy1TjPqgwDQYJKoZIhvcNAQEL
 BQAweDELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0xOTEwMzAyMzAxMDBaFw0y
-MDEwMjkyMzAxMDBaMHgxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
+Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0yMDEwMDMxNTMzMDBaFw0y
+MTEwMDMxNTMzMDBaMHgxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
 ZTEPMA0GA1UEBxMGV2Fyc2F3MSUwIwYDVQQLExxub2RlIGV0Y2Qgc2VydmVyIGNl
 cnRpZmljYXRlMRswGQYDVQQDExJkY3IwMXMyMi5oc3dhdy5uZXQwggIiMA0GCSqG
 SIb3DQEBAQUAA4ICDwAwggIKAoICAQDib61eHI9KxUqLPqI/KeUVB1VmNsuu+lxR
@@ -21,10 +21,10 @@
 BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUH4gHxXmo
 ZnxR/Tf2BJMylqE9YYAwHwYDVR0jBBgwFoAU8Vm4Zk0+w+dC7oGj1EZ/OVN1+Q8w
 HQYDVR0RBBYwFIISZGNyMDFzMjIuaHN3YXcubmV0MA0GCSqGSIb3DQEBCwUAA4IB
-AQCEm9hL/m9BTXnxILqq++2JjDzh+ZLjorgNQ/TdHEGhx9bm1SxaR/bT5uOLANEp
-I+pUTPC6W700F6FizD5IhHreOUwKICc9e341W7GrVPbBsi583PPcDVIYL/0EtEus
-Rs50oDaPiBeOf9Tix9YxcjtVtGHBKXkEtikxwS9XX26AGCLeBJOd3rIclm/vaCNJ
-R4Mn7NsjRAe2b0+JFmyQrdZXqGBYEzfIV5nb3+1O0S0ppVzmDWUJYTjtujzxvP6w
-xwAjCJ06ZFjqaYP2VksqFt+7kGKLOhhs8VnRz5EExkNtmzOszZvAn1M/o4vZX0pZ
-NU5Tt5/J8p6gNzvSRcjsJyI7
+AQAnL07txl6GnhEsgzZrZHlCqz/x4vsDwg0v9Zk4miJEmd0Qv2QJwrr4kl2ZhL0a
+93fpkrmMM8t/eWztjbQqXwW6YQGg23xByLTfNRs1CZ2lhgGuFid2GLEG96TqBj3I
+cO6wULazT40rwws5yHpGI8I1P5HlHyLOiM7V+5EosHz3lyKxBI2/qvETPxIsmt8G
+M1UUG3xlF1BWcW9/9EbOikBSiEzLVOEiOoaPI1zXBRmHI0fZDyMdCdOVob/fccmg
+ZGrxY6wnlrNPyhruk+2B3yFW6wwOt3/Fh51IBes3Oxu0EwO7qQhaik3xbL6pKH92
+6NPMYWG0QiC5WvJnoFobnHf+
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-dcr01s24.hswaw.net.cert b/cluster/certs/etcd-dcr01s24.hswaw.net.cert
index d8dccd2..e8711e8 100644
--- a/cluster/certs/etcd-dcr01s24.hswaw.net.cert
+++ b/cluster/certs/etcd-dcr01s24.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFHjCCBAagAwIBAgIUKe9dtX3sAAushUrH7/r7N3UoPdwwDQYJKoZIhvcNAQEL
+MIIFHjCCBAagAwIBAgIUSMVQ8VRSx+yQhCKL3lHv7taBIvgwDQYJKoZIhvcNAQEL
 BQAweDELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0xOTEwMzExNTE5MDBaFw0y
-MDEwMzAxNTE5MDBaMHgxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
+Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0yMDEwMDMxNTM4MDBaFw0y
+MTEwMDMxNTM4MDBaMHgxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
 ZTEPMA0GA1UEBxMGV2Fyc2F3MSUwIwYDVQQLExxub2RlIGV0Y2Qgc2VydmVyIGNl
 cnRpZmljYXRlMRswGQYDVQQDExJkY3IwMXMyNC5oc3dhdy5uZXQwggIiMA0GCSqG
 SIb3DQEBAQUAA4ICDwAwggIKAoICAQCl0NRZ5k7SgPeq7v0L/yGjgW8ifJV/3HJQ
@@ -21,10 +21,10 @@
 BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUyyvX/j6W
 TiyCWhZjXwJavrVm+McwHwYDVR0jBBgwFoAU8Vm4Zk0+w+dC7oGj1EZ/OVN1+Q8w
 HQYDVR0RBBYwFIISZGNyMDFzMjQuaHN3YXcubmV0MA0GCSqGSIb3DQEBCwUAA4IB
-AQAzX8owYwbspNIfXBzs+xR2kkoJH5IHHo7lU40QJUYjmg4HqHHQVne842gaXkq4
-LA5k0xAeHIFf4AmwaJKmJUchfYf3Kdrz0bgQ+w7P6fXN1JmMJUV6lWdrmaZrWBKA
-dhfstlnBsaNBNx1FZ08/gWlnIw5Vpyz5F/5oxZqnvKCONFi35t5g/IwUxMNh8tu6
-7z3+KhyNkIHEX0OkOz5XnGcmn2jwEoqOoX3x84NWxTq5rUefBptRZMpgL0qmxOEp
-at0ftNY4DHYpEMG7MiHIPE6pElIzBHQpRRkg6VZBU4YUXCGrG5htjde8DxNMdcdA
-csgzP8tQpoQU3ZriCKrDYN7W
+AQB1oYVNWzed6MAzvnkeB/4oj14IMbJF+iF7uXc/lAAoOzeJ0JetOuq0HuMJGBq2
+Qrk4zva9gR+CbYzSTsc8MHxCI+/rf/ywHTdOJkGUmyF9K1grLuKNvM/zT0v5zGwq
+6u8+Nxbi0W0NmRjht2nQhkRBn8WEhyAAoqzITz8GRPcTPwzlsEh0FBt2rt/vQgkL
+Lny9/W6BIX+3UjhlgeludY3eFfhr79cSabWL33v+MJx89RPWKdDYW6tW+w8sqQsY
+K7wHUaZNB5crW6/EZIG0ICHHd+IVHCFOJ/OyubT/rjhP/5AnjJHQpZhATLVN8toY
+dkMnauFLQIdiSFaKsF5GEFv5
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-kube.cert b/cluster/certs/etcd-kube.cert
index ae19bd5..dd0b91f 100644
--- a/cluster/certs/etcd-kube.cert
+++ b/cluster/certs/etcd-kube.cert
@@ -1,29 +1,29 @@
 -----BEGIN CERTIFICATE-----
-MIIE8zCCA9ugAwIBAgIUfFMQAZYna+HSa0hSJnqmmJyfSw8wDQYJKoZIhvcNAQEL
+MIIFAjCCA+qgAwIBAgIUSc+yoWMgxteBtdswAaa+RZmh6hwwDQYJKoZIhvcNAQEL
 BQAweDELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0xOTA0MDYxNzU5MDBaFw0y
-MDA0MDUxNzU5MDBaMFsxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
+Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0yMDAzMjgxNTE1MDBaFw0y
+MTAzMjgxNTE1MDBaMGoxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
 ZTEPMA0GA1UEBxMGV2Fyc2F3MSUwIwYDVQQLExxrdWJlIGV0Y2QgY2xpZW50IGNl
-cnRpZmljYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw5jdxQxt
-ELgpg+M1jib4VklzIJ4ULrKH59xKxs6qK0iGJClc8EFvVEBiuNpqXQ6Vygb8vbGz
-lplo02ivmhPVIFZ4ymk46kxG+w0scNcm/wMAzL1RiJT2nD71eMzYhVzomF5cQRGo
-JvqNpQg88jbTFluqHNFYTkv1HAnS+OMu7sbIm9iGgNfIoBrn/JpV97Rf1x4CaQ3t
-NdlrDxihQcMI4xoG2deIJdxFI6z9Rh7s4nXjBaAz9i8cRJ9v4uaMRq6kv3mBEfki
-Ve2Ql7jHTHwMqS/CsemkSl6IAG7IOQLB9U8xKsFuFPiYX11Xghcoi0MbjH4Qwocw
-vzDxPuttxNfWOr8uy70tCHWJWOajZWKtjj6+z9J4baTpjUu24y786qNkR8OVhjVw
-W+ETlf1I4/dUJN8iP4Zv4ibseJz9EhJZ41jY6+73bZwRao0lKig9Z69538r1wFs/
-1zOJP9YSJnuGA+rIYgdsu1fsq3eUWqJdlEAwpyx1TxfUJvFgx9ni0YfCUhmSb0B8
-b5Jt1TU0Lk1arZJ3NE1qC8gdbY4V+8MEKKfyq5uIlzaLrOQokUo5panfRGxxAFfe
-Y5QZb4jpXmx1W31hb2V1NY73fy2o/4JhEZcpfmjdhoDCgBwlKP2Xf6AFo/wtLdZF
-QhAVmDg5Vy8vcU381QSS08DRysptAZAhIYcCAwEAAaOBkTCBjjAOBgNVHQ8BAf8E
-BAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQC
-MAAwHQYDVR0OBBYEFB3FpZrvkz7oq25DKwzCMiKCCsgMMB8GA1UdIwQYMBaAFPFZ
-uGZNPsPnQu6Bo9RGfzlTdfkPMA8GA1UdEQQIMAaCBGt1YmUwDQYJKoZIhvcNAQEL
-BQADggEBABNwEAoXdjn7fmFTopWrOPK1fw9fHNsLbD5MJt14Gj2XZAirHHj8sPDQ
-Y3SdUhCnI1CUS4TccDGZBVgCIQ/grr6fvXe2rZPnh8n5rfxbUvWhqey4OKekzjEV
-kPPtDZOGqa70jFdlYHjqMPfB1oR6yWJCt7CD5yeWkMlsfnOOI1xU4+2sbOrlkCDa
-FYmywe+m6nEJFh/AJ6luElkOw4XrkV778JzXg6O4qyCWCEqSkgOXLOQqhOfc/qGk
-1YlsSf5AT7Ual2/tYaAW29zPunaZ/jZP8dOBN1r93QYinnnh1E68sOYEsAig3Aff
-nr7TT4YwEp62SkNxAyDVkA1WXC2kTTY=
+cnRpZmljYXRlMQ0wCwYDVQQDEwRrdWJlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAw5jdxQxtELgpg+M1jib4VklzIJ4ULrKH59xKxs6qK0iGJClc8EFv
+VEBiuNpqXQ6Vygb8vbGzlplo02ivmhPVIFZ4ymk46kxG+w0scNcm/wMAzL1RiJT2
+nD71eMzYhVzomF5cQRGoJvqNpQg88jbTFluqHNFYTkv1HAnS+OMu7sbIm9iGgNfI
+oBrn/JpV97Rf1x4CaQ3tNdlrDxihQcMI4xoG2deIJdxFI6z9Rh7s4nXjBaAz9i8c
+RJ9v4uaMRq6kv3mBEfkiVe2Ql7jHTHwMqS/CsemkSl6IAG7IOQLB9U8xKsFuFPiY
+X11Xghcoi0MbjH4QwocwvzDxPuttxNfWOr8uy70tCHWJWOajZWKtjj6+z9J4baTp
+jUu24y786qNkR8OVhjVwW+ETlf1I4/dUJN8iP4Zv4ibseJz9EhJZ41jY6+73bZwR
+ao0lKig9Z69538r1wFs/1zOJP9YSJnuGA+rIYgdsu1fsq3eUWqJdlEAwpyx1TxfU
+JvFgx9ni0YfCUhmSb0B8b5Jt1TU0Lk1arZJ3NE1qC8gdbY4V+8MEKKfyq5uIlzaL
+rOQokUo5panfRGxxAFfeY5QZb4jpXmx1W31hb2V1NY73fy2o/4JhEZcpfmjdhoDC
+gBwlKP2Xf6AFo/wtLdZFQhAVmDg5Vy8vcU381QSS08DRysptAZAhIYcCAwEAAaOB
+kTCBjjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
+BwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFB3FpZrvkz7oq25DKwzCMiKCCsgM
+MB8GA1UdIwQYMBaAFPFZuGZNPsPnQu6Bo9RGfzlTdfkPMA8GA1UdEQQIMAaCBGt1
+YmUwDQYJKoZIhvcNAQELBQADggEBAHGsNqvBFSQC8qWqTTCtghbmF6nQqyhwEgsn
+L/29QMgmNx234r41JPymEN8R2bMFHuMDrOgEliXcbqcirpOuCvx3nln8gFmimtN5
+q7MEjAllJHqa1sjx+O83TNToFkmc8gxUsDqdngrsS1IoEbs8tIP7P/Y80gshCUfz
+2Fnqv1/m8h04RJ7E8Vgxq9txR3JmZPWbFtiHTb7Izlv6y9c+mtCDwzFb8ZiXCfSe
+TFBOptEjx5S82kj7LgYvtngz99ZojoCeQnqczbnHagw7yLH+NqF1SpTr6xZKzdQ+
+hh5Zna0whQCAy2wF5paW3asPNb7Sh90xXARynYxuvCKAIeurAI0=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcd-root.cert b/cluster/certs/etcd-root.cert
index 14b4897..604ff83 100644
--- a/cluster/certs/etcd-root.cert
+++ b/cluster/certs/etcd-root.cert
@@ -1,29 +1,29 @@
 -----BEGIN CERTIFICATE-----
-MIIE8zCCA9ugAwIBAgIUDCr9SS9iUS+70qRrwt2yhJ0kJjUwDQYJKoZIhvcNAQEL
+MIIFAjCCA+qgAwIBAgIULK8H0d1v3xxIrRUgoGwW+5x6GIIwDQYJKoZIhvcNAQEL
 BQAweDELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0xOTA0MDYxNzU5MDBaFw0y
-MDA0MDUxNzU5MDBaMFsxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
+Y2x1c3RlcmNmZzEQMA4GA1UEAxMHZXRjZCBjYTAeFw0yMDAzMjgxNTE1MDBaFw0y
+MTAzMjgxNTE1MDBaMGoxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tp
 ZTEPMA0GA1UEBxMGV2Fyc2F3MSUwIwYDVQQLExxyb290IGV0Y2QgY2xpZW50IGNl
-cnRpZmljYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAudCBVJv6
-6aRyizfh20/PeIt7iSP9HpTorxxtG0Ti7ybjhu7gpkPYAK4Zf6YyYPgIjmv2hagp
-1iCO/PZYlqUL73x3QyfRFrAighRcEh/v7zMGbgIrZPO2ida7A7q4V0VcPaK/zPNt
-0gAlcoZSXuD5WDKCdFA9elDRLpdsgtDe7BjTgBp0MTzj2NBUoTRso36Depxuh2Og
-nvQa/nGhAOV5GCovMQ6PA19mhnZnK+JUZXwYWSnOfzayR7Rjy3eRsnoQq7x7Dur+
-RAnX9BDXUObULJgA6cHpEfxU/fnpCqWXZOWYgougeWdR9C8VyB0G7neVQDZGDYSV
-x+VH7D22DPLmmqRAgsFs7aF6E1Yp15no3P2kTxHEYPcMus8aNOZgs4pgeiFwPmu+
-aT+vW2mf82IHjPPAFfhkrzBodS/LjHDpAFklb+/wp/ypMYtCuXucm9kbV0oXxx4l
-a0qAYSAfL13wHTU6WXs9WAMEeqqQqLYRmvmssVtanYJUaRNZjCOYzTTi5gILUTLz
-BBPxgTpLsbCO97EueVrAer4s9C0dBcYksRXs024+N//g4pYyJAIuI/ZRJIbtHItk
-mw8mdwizynXtkRtvWl2lV1qHilo+p3xhSrh0vFBGO+wuT8aragUdbC/XGDoecHBr
-7KDZIYx7cZw2vI9xMqujP4hLhG9d6Zko810CAwEAAaOBkTCBjjAOBgNVHQ8BAf8E
-BAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQC
-MAAwHQYDVR0OBBYEFDDZWVeg57MP21XCkTRqzFR2OmttMB8GA1UdIwQYMBaAFPFZ
-uGZNPsPnQu6Bo9RGfzlTdfkPMA8GA1UdEQQIMAaCBHJvb3QwDQYJKoZIhvcNAQEL
-BQADggEBACzrx2h6qwfEcqJl06Epd6BEbQnZZlVhte3/lX0MSh86hxw+qxAPwfrG
-2XdcjXee6OCpAZv3qP/dRWnjKoKbIPNIeEK72n5pwwYbaQkhsHCm4XGgRyj9MHOo
-Ua0t0tXHESoD7uP57E/Q4CtcHOpR3pUUhKwqFB3NUvo3hZw5F3fKGoRSLSK3Mdwq
-sxxvTcYXxp3kjXm2cjTaZMoQ6eLBpEL+gCGezdts6+ExXCCRMWSdeQhhciv0Ez65
-6mzCq1uLGfQs1qd3eycPBi2Tt0vZ+Iitei+deewzwfpZ3oPCbI39kY1bxnIUNU0P
-Jr4JXnoB8k8ZTXsi15yom38pUy0xJhc=
+cnRpZmljYXRlMQ0wCwYDVQQDEwRyb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAudCBVJv66aRyizfh20/PeIt7iSP9HpTorxxtG0Ti7ybjhu7gpkPY
+AK4Zf6YyYPgIjmv2hagp1iCO/PZYlqUL73x3QyfRFrAighRcEh/v7zMGbgIrZPO2
+ida7A7q4V0VcPaK/zPNt0gAlcoZSXuD5WDKCdFA9elDRLpdsgtDe7BjTgBp0MTzj
+2NBUoTRso36Depxuh2OgnvQa/nGhAOV5GCovMQ6PA19mhnZnK+JUZXwYWSnOfzay
+R7Rjy3eRsnoQq7x7Dur+RAnX9BDXUObULJgA6cHpEfxU/fnpCqWXZOWYgougeWdR
+9C8VyB0G7neVQDZGDYSVx+VH7D22DPLmmqRAgsFs7aF6E1Yp15no3P2kTxHEYPcM
+us8aNOZgs4pgeiFwPmu+aT+vW2mf82IHjPPAFfhkrzBodS/LjHDpAFklb+/wp/yp
+MYtCuXucm9kbV0oXxx4la0qAYSAfL13wHTU6WXs9WAMEeqqQqLYRmvmssVtanYJU
+aRNZjCOYzTTi5gILUTLzBBPxgTpLsbCO97EueVrAer4s9C0dBcYksRXs024+N//g
+4pYyJAIuI/ZRJIbtHItkmw8mdwizynXtkRtvWl2lV1qHilo+p3xhSrh0vFBGO+wu
+T8aragUdbC/XGDoecHBr7KDZIYx7cZw2vI9xMqujP4hLhG9d6Zko810CAwEAAaOB
+kTCBjjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
+BwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFDDZWVeg57MP21XCkTRqzFR2Omtt
+MB8GA1UdIwQYMBaAFPFZuGZNPsPnQu6Bo9RGfzlTdfkPMA8GA1UdEQQIMAaCBHJv
+b3QwDQYJKoZIhvcNAQELBQADggEBAD+Hf/kVNyXPbWL7asmMsqcldGocnzVFDK8J
+91pWLPSdbAexSUwP6sq2yHUPYMH4uVwjQOg3nKR9GImRJpHkudwZ8M876VdqmCBS
+/KgwlCWQtoN6cw9fQXGnPlJA+LN4q/YBQv0KRN1/eL/jKMPZZL3f0Hy+/4uOvK40
+L2RgNcoXhvRWsJRN+xf00ZvATiHxyhq/uC2dfTgpdCFynl1X700Z6Mk600J6vbo/
+FtGdj6F7nKwi00g2236tb3BEaL1vl2xtdm36xIDmX6F23p3dtRdIl1ULPFg1qAoa
+g8QjcUxILkhwadgKmiUDxVPA+1/afYRklPcHGziB0VJfOTgz1Rw=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcdpeer-bc01n01.hswaw.net.cert b/cluster/certs/etcdpeer-bc01n01.hswaw.net.cert
index f1e987d..0c3d762 100644
--- a/cluster/certs/etcdpeer-bc01n01.hswaw.net.cert
+++ b/cluster/certs/etcdpeer-bc01n01.hswaw.net.cert
@@ -1,29 +1,30 @@
 -----BEGIN CERTIFICATE-----
-MIIFAzCCA+ugAwIBAgIULQDI1WmHgFvhWsA2mhX9rdtXO8IwDQYJKoZIhvcNAQEL
+MIIFHzCCBAegAwIBAgIUbtFJibIQ+L3FalsNIadl2cqxzs4wDQYJKoZIhvcNAQEL
 BQAwfTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTE5MDQwNjE3NTkw
-MFoXDTIwMDQwNTE3NTkwMFowWTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
+Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTIwMDMyODE1NTMw
+MFoXDTIxMDMyODE1NTMwMFowdTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
 aWVja2llMQ8wDQYDVQQHEwZXYXJzYXcxIzAhBgNVBAsTGm5vZGUgZXRjZCBwZWVy
-IGNlcnRpZmljYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1DgM
-53WoGjOczAv1tyaNnJ2bQLjlE9iEWxS23xHr54VTEw6c/lG+L3E/XB0jf4CJlBR9
-pg34PhTEbcXOm9H2Mh5U5W+y6Q+mCu2FdKK5raj8bxbYGNMhDzg98bd2PqDxZFfk
-TsVqzzLegRDus+Q1AMMWkHxFLxuQMXTqUd053Gmd6a6RkwjqewmRy3+id9uzRbyn
-Ey9ML6KGlPTWvMgqEYvvgwGz05wtSZEMp0pheFkpPetegC25GQ5KQCe2a2zJ6eFW
-3IpiIwmXyC7sMINGDFjgTbjwmVLSEaVOgYX9qdYLpXNlzTgdxA61AlV5LSF0M4VJ
-LpsrK6ec1pcddyy4LwzAPAJY3B33xvulaSD+BxpdPO2S2B2/tLmhhhC7DGv+UewL
-QVZ+yPACMSUtmADhPdIBOwMUwnxaw4WKpCRpU/5OhSfV3OKZD5ejJS34H0YpXAPn
-gq/Td0kL3suIGUBKFCUMIQ3qkErucGoGWF5HBDdOA7vTAo5Q4yWXgumLU6kMXG5o
-4Lxc0+jszX8MprlQ1Oj7c2qfM/M0tbarEpaiSFcNyacJNl/uiuNP8jUGhdu+R5ey
-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
+IGNlcnRpZmljYXRlMRowGAYDVQQDExFiYzAxbjAxLmhzd2F3Lm5ldDCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBANQ4DOd1qBoznMwL9bcmjZydm0C45RPY
+hFsUtt8R6+eFUxMOnP5Rvi9xP1wdI3+AiZQUfaYN+D4UxG3FzpvR9jIeVOVvsukP
+pgrthXSiua2o/G8W2BjTIQ84PfG3dj6g8WRX5E7Fas8y3oEQ7rPkNQDDFpB8RS8b
+kDF06lHdOdxpnemukZMI6nsJkct/onfbs0W8pxMvTC+ihpT01rzIKhGL74MBs9Oc
+LUmRDKdKYXhZKT3rXoAtuRkOSkAntmtsyenhVtyKYiMJl8gu7DCDRgxY4E248JlS
+0hGlToGF/anWC6VzZc04HcQOtQJVeS0hdDOFSS6bKyunnNaXHXcsuC8MwDwCWNwd
+98b7pWkg/gcaXTztktgdv7S5oYYQuwxr/lHsC0FWfsjwAjElLZgA4T3SATsDFMJ8
+WsOFiqQkaVP+ToUn1dzimQ+XoyUt+B9GKVwD54Kv03dJC97LiBlAShQlDCEN6pBK
+7nBqBlheRwQ3TgO70wKOUOMll4Lpi1OpDFxuaOC8XNPo7M1/DKa5UNTo+3NqnzPz
+NLW2qxKWokhXDcmnCTZf7orjT/I1BoXbvkeXsp13EQ6TV7ig3WcXwM61d4qaqbtt
+mK1TGvL7hNR1C/Vn23kEd7GD2OQhpqKwP3t361IYtulMCZ0rBWckLL6xeIhA0J/w
+jY2YqZ/kzPGLAgMBAAGjgZ4wgZswDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQ+AVJ4
+b1WbetRtLuoHoZkRG3TyJDAfBgNVHSMEGDAWgBQte5qvaKoZpMwqiL74duS9J6ck
+DjAcBgNVHREEFTATghFiYzAxbjAxLmhzd2F3Lm5ldDANBgkqhkiG9w0BAQsFAAOC
+AQEAoeQruvpOcvGc7qr1OWFycJcwEd8k4o/1Oc5KPEKlVDNf9XcweJOetAayBMMK
+Y3lGkBuOHQ5Crx8kXruiQyi6c7tUd9rVtDWWwcLAR20CkDCYnJNn46djgRR6J4pb
+RFz+31cIDgQT2GJhO2waajrcqfrBZeuDyyqt3ZUn3hpICdTbYJWV3x/vqLIY+FpT
+Z+x8Muzb0EWFXCBxZBHsZXBuoCjtmmrNrf0ek5Ag+n5fxl1AdGAuqD4D01wJWa0L
+UJtaLmuD02Pw/c6RJ5vkWI9DRcH7/XdYZZ5yf7Ch2GNivKsz9ekIVpVz0HJMOVPM
+yw/s8FL0AczmICjx6yjlu4RQ5w==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcdpeer-bc01n02.hswaw.net.cert b/cluster/certs/etcdpeer-bc01n02.hswaw.net.cert
index 25d9d57..8776183 100644
--- a/cluster/certs/etcdpeer-bc01n02.hswaw.net.cert
+++ b/cluster/certs/etcdpeer-bc01n02.hswaw.net.cert
@@ -1,29 +1,30 @@
 -----BEGIN CERTIFICATE-----
-MIIFAzCCA+ugAwIBAgIUOrI3Jbcvd5z7rjuvd/ZCJa/k6C4wDQYJKoZIhvcNAQEL
+MIIFHzCCBAegAwIBAgIUHPJfFvMMibUlrbwagmA8Zy9KtS4wDQYJKoZIhvcNAQEL
 BQAwfTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTE5MDQwNjE4MDQw
-MFoXDTIwMDQwNTE4MDQwMFowWTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
+Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTIwMDMyODE2NDUw
+MFoXDTIxMDMyODE2NDUwMFowdTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
 aWVja2llMQ8wDQYDVQQHEwZXYXJzYXcxIzAhBgNVBAsTGm5vZGUgZXRjZCBwZWVy
-IGNlcnRpZmljYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxZ9m
-liz7Af+qD3n4peAg8wNQ3l+7CalN6KJOnOlyTom371pcD59O1Uz47+/mWR5/dfQC
-2Pc42KxWOOh4YZ3zLdYNoNqS6XkyR40n3uc5an6xJYOaCQbvAA1ncm11AmGIoBl2
-BKOC/FiYQr+RsplOtN7NGOYNLLxit6hCm7Drwqu7Za2JR0YDffJZ6kNtaWK+5M40
-pR/coCUa4Cf94m4LhsLVbitDGrXtCmQOQVQP4Vf/buu8UO+e+rgGEOnS/kRpBiVI
-R+anD+uVYSYQPPhMWfDi6JDV561Kep+jvSkFX4NWz17PuTSiiAD+0iyvxPXSRDRx
-2lylu/IvOn9rkVL1kSfDW1ArLGlSbd3qyt4XKLCgIMhHLQS/KnmIOhOnhL9ZsJBk
-eq9BEgEIZa5lL8dTOwsKW+7NqXWr71aBSli/VaLlth+VvPxlhkROLW/6BleEsgkh
-/t4TZ2VcRS0+FWQgys4/btSJR+lKcMEkoBra9WXEX7fitrLWK+9jO6PXVf6X0Y8c
-bWLfuquaE2s4l7uwGnlVmsGBd1978L9cTGK554C+Fzjr7blkCpTUleGh0bXNP0jD
-S5+Iu4AHShP5JAsSAyLMjskrSHhltcWh0D7GSdz5HYyRKNEjxg8lFmcH/U3a05sk
-BbW1vVECWI9wyGw12g8tfrtgiE61E4+d8o3aJOkCAwEAAaOBnjCBmzAOBgNVHQ8B
-Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB
-/wQCMAAwHQYDVR0OBBYEFDOk2Cyznv+t3Gbzw/FmmcVS8iJPMB8GA1UdIwQYMBaA
-FC17mq9oqhmkzCqIvvh25L0npyQOMBwGA1UdEQQVMBOCEWJjMDFuMDIuaHN3YXcu
-bmV0MA0GCSqGSIb3DQEBCwUAA4IBAQBFc02XSE2W7e8ymZYcTlJvzo6jJllAf7Hz
-7QsyiRoEeFfGE6YpI+sr/isRNjpT6QtZnhB03N3wazYjBpD2otrTaOabJLyIchhS
-pJjLPHQUXRukWQTv2regnNpGmZajBIlZhNafy3p98JG6xP8ZTKRfnqddFPPEeRPB
-YH7IyskbrjZAvmpmLWINO4+mPFLsK+gVfqOhZc7pMDt64fD4DxDKo/OyfF4YY4Qm
-ndhfi6iDeAp1I9b8dVq4o2nh4mx+Qd6URASbjPChO4I3rOXL+VUFKkY6dRjBeLBn
-3vir+WTOqWEGa0ubRGqppTGwiH0y1YSgZNEeLWi5Ujnmqt6ZfBcn
+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
+aKEgxEqoYvpE9ls1tSaOXS2iLw==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcdpeer-bc01n03.hswaw.net.cert b/cluster/certs/etcdpeer-bc01n03.hswaw.net.cert
index cf32ae4..a67d385 100644
--- a/cluster/certs/etcdpeer-bc01n03.hswaw.net.cert
+++ b/cluster/certs/etcdpeer-bc01n03.hswaw.net.cert
@@ -1,29 +1,30 @@
 -----BEGIN CERTIFICATE-----
-MIIFAzCCA+ugAwIBAgIURYvLd+Fg/5bnkj7FhFfSNAUFfu8wDQYJKoZIhvcNAQEL
+MIIFHzCCBAegAwIBAgIUY0PqPfxno72apj4xsBsQPC/QTbUwDQYJKoZIhvcNAQEL
 BQAwfTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTE5MDQwNjE4MDUw
-MFoXDTIwMDQwNTE4MDUwMFowWTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
+Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTIwMDMyODE1MTUw
+MFoXDTIxMDMyODE1MTUwMFowdTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
 aWVja2llMQ8wDQYDVQQHEwZXYXJzYXcxIzAhBgNVBAsTGm5vZGUgZXRjZCBwZWVy
-IGNlcnRpZmljYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvsau
-00vTLtwruq7TPh3xZ1Y5NZvOUFfF3H48wOeFZ5U421VRpD7APUmzt713WbIi6Nl+
-W1ac/MDg7KYm2MjcKYWKn/1LKVGCBoa0doXude177Jqgr5S9zsAP69GMKeZ2cmBT
-PfKgCiAScbF15bDLwv9CcYZg2/GRuh4fdbEuSoFb0FZiwNwMrW5XMSzN+lsVhXUJ
-oKrNDjnkazKMWq5FninCQ8mnnEwFi9+j/aN/UCJYMf7BndIHIvrLGrSyQUJwQD8M
-lPL6CBhW2aOfoLUwMeJBSdYVVH5/QIsNH1XoaZBtDYZs6BjaMtCcTN0p2/cFz5BV
-4yz4WP7CCMCTW+64rAD/M8JZO1s9HQfc0jVEqXfjXVSAEnUJQ+YlYGxGK2oyAWA8
-iF4raLArp8yFbW0SndX2WQ1T2hs2wJp6n5GXD1zTl0oGBq2PxOI8l3llPLDlKcL5
-RnrS713TC7KS7pTr76R5WmrSxtWzmo/jMICSg5ysm3UcjJL9rTM9cEc9OsDCTTtp
-nfZfwae61607m1QzTf7o641jrlA1be/itg/uNbQ2+0pmjwpe+ZWf/WyW0aFhWoZW
-qpkq+SFtAU3dEh9kxkpSf53zWkDZwwBhe0aqfV9w4Uh9cEQXapam86tXASDfik1F
-TR/C+qHrad8iTgvaIHMjiumP4+8IQjncMhCVUuMCAwEAAaOBnjCBmzAOBgNVHQ8B
-Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB
-/wQCMAAwHQYDVR0OBBYEFJCMTpAkSKDHOps8B+BIk1Af4xfqMB8GA1UdIwQYMBaA
-FC17mq9oqhmkzCqIvvh25L0npyQOMBwGA1UdEQQVMBOCEWJjMDFuMDMuaHN3YXcu
-bmV0MA0GCSqGSIb3DQEBCwUAA4IBAQBnXInlyte2V8TJ4/YF4VEonyeQR2lNv/Xs
-ZH+8dpV1RLJ0bharklu5fYcGGbVyeFJdnLu/NkDuEhVtw1dSu2NcMtNAvGiCBi7C
-5V45zZ9OLBRExSXz+L1ZDmbORZ5vBNyNWEEish0mptu2cNf0krXcwfSQ8n7caBHU
-CterMKIGS973/125yduu+tELVOvUGvRNqplTwRUrdyj2dL4gkirbE3nhIYbidK4B
-tm845sDVzguosoAJPAEsgXDw8NV7SZxR7WsFKsA8NTWB8L2kJ1wNL44cygDuGsL0
-5xKbTb79ePq0tSiNJ7KZXArhOIKRem3/iHikiD6Msbt6SuDKYrZB
+IGNlcnRpZmljYXRlMRowGAYDVQQDExFiYzAxbjAzLmhzd2F3Lm5ldDCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL7GrtNL0y7cK7qu0z4d8WdWOTWbzlBX
+xdx+PMDnhWeVONtVUaQ+wD1Js7e9d1myIujZfltWnPzA4OymJtjI3CmFip/9SylR
+ggaGtHaF7nXte+yaoK+Uvc7AD+vRjCnmdnJgUz3yoAogEnGxdeWwy8L/QnGGYNvx
+kboeH3WxLkqBW9BWYsDcDK1uVzEszfpbFYV1CaCqzQ455GsyjFquRZ4pwkPJp5xM
+BYvfo/2jf1AiWDH+wZ3SByL6yxq0skFCcEA/DJTy+ggYVtmjn6C1MDHiQUnWFVR+
+f0CLDR9V6GmQbQ2GbOgY2jLQnEzdKdv3Bc+QVeMs+Fj+wgjAk1vuuKwA/zPCWTtb
+PR0H3NI1RKl3411UgBJ1CUPmJWBsRitqMgFgPIheK2iwK6fMhW1tEp3V9lkNU9ob
+NsCaep+Rlw9c05dKBgatj8TiPJd5ZTyw5SnC+UZ60u9d0wuyku6U6++keVpq0sbV
+s5qP4zCAkoOcrJt1HIyS/a0zPXBHPTrAwk07aZ32X8GnutetO5tUM03+6OuNY65Q
+NW3v4rYP7jW0NvtKZo8KXvmVn/1sltGhYVqGVqqZKvkhbQFN3RIfZMZKUn+d81pA
+2cMAYXtGqn1fcOFIfXBEF2qWpvOrVwEg34pNRU0fwvqh62nfIk4L2iBzI4rpj+Pv
+CEI53DIQlVLjAgMBAAGjgZ4wgZswDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSQjE6Q
+JEigxzqbPAfgSJNQH+MX6jAfBgNVHSMEGDAWgBQte5qvaKoZpMwqiL74duS9J6ck
+DjAcBgNVHREEFTATghFiYzAxbjAzLmhzd2F3Lm5ldDANBgkqhkiG9w0BAQsFAAOC
+AQEARkvDE+dGvSXWF8iHeXrasd+MoZz6+ifpFA27dsGLDfbEfmbks+nbJCAGhIqf
+YqKJ8H+lopIBDtxbcRfTjXDLsncmNTrWf/FVEx/WLbGQsYrqncW8ym2Xz1qBCMde
+/QBKKXSEEiBpVLYn0+41dvtxw8nAfIPin98pScnY04p+5BGaxFsKb2CsshBJlDOR
+g7BFYiWpQz2mAr6tyO/lG2KqN0B07P07jszej0c8Xl+tkbMtpghIi2GcJ75VXNJf
+XRtmeTw7YqcLgKTknBfJ9+GotmFUfAF2f+jA21URWY/6f4Yr+BniZj6Ikahx3eD+
+fw+y3c4eBLl5G3WugIFj5cM0UQ==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcdpeer-dcr01s22.hswaw.net.cert b/cluster/certs/etcdpeer-dcr01s22.hswaw.net.cert
index fd535e5..f6bf5e0 100644
--- a/cluster/certs/etcdpeer-dcr01s22.hswaw.net.cert
+++ b/cluster/certs/etcdpeer-dcr01s22.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFITCCBAmgAwIBAgIUGIumkbssnFQwYrQT8xCHqhU2DJwwDQYJKoZIhvcNAQEL
+MIIFITCCBAmgAwIBAgIUWVNkuf6gx/r3S5q+rpSq4rMhffswDQYJKoZIhvcNAQEL
 BQAwfTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTE5MTAzMDIyNTQw
-MFoXDTIwMTAyOTIyNTQwMFowdjELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
+Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTIwMTAwMzE1MzMw
+MFoXDTIxMTAwMzE1MzMwMFowdjELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
 aWVja2llMQ8wDQYDVQQHEwZXYXJzYXcxIzAhBgNVBAsTGm5vZGUgZXRjZCBwZWVy
 IGNlcnRpZmljYXRlMRswGQYDVQQDExJkY3IwMXMyMi5oc3dhdy5uZXQwggIiMA0G
 CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC8CX6AHfDOCXrNacddsGApq2PZMQ5w
@@ -21,10 +21,10 @@
 BggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU1Yhk
 16mTklzv3gvOdw38WrodcA0wHwYDVR0jBBgwFoAULXuar2iqGaTMKoi++HbkvSen
 JA4wHQYDVR0RBBYwFIISZGNyMDFzMjIuaHN3YXcubmV0MA0GCSqGSIb3DQEBCwUA
-A4IBAQBXo4Yj2HN3TvQMy662Vm+hLyD/aL0L4FVA3dzTl47LX+7P5uL6e5OaES1A
-PSbiIJc75zcSmpdOZHQgUZTEfUZWjZVuE9Vobe7TgIdxB5kUrKvvgqR/d3t7t+B+
-3RayeTO6wlErVj/FffDuy664AVAhXsAC/38iDu7uyAnmNcORELn4kadwZ9FyjWUh
-x44o5+7AJXX0TldXMH1WHh4nwHdOlTeAf147iTUSvLp1QJu9X4EfaeF5MmfqXdGN
-lyjKfbs+Mzt93aNudpiOXolAUdI2t+jhTCo4BA/48QhKwtQZWdGbBWvCdsamZwHU
-7/lijK2qFFkN7xv4I0klSsxyS0Qs
+A4IBAQCV4L9ABnxG2salwBi+t17KvcxEUvo0oD0fs8tI+F5aQB3pHQ/NqRFd/jw2
+7yA1TXt0WG5VBDwbWzJU0psSH5IbPgN5DYVh0BDw71rFB92I3qtW+g5WJo0S7YPj
+GakvJaQmoXPc8ayYFE0n4aFkV9rUVQvcUNjmBsX+rX5su2SbVnE0ZXM6EKrepiFI
+l4HEGokkrOAHKs+1rpJcq/L2e+bRwPmGmTBpnmxlb3AFLST+nL57DZFqho90hFlC
+mifNsSJwXFfDy6lY0XIe+ZyuwfQghfKQLjzee1lgLZIAIdbkFbsP3wh2Z9hG08OL
+pHUSOfG21sh1AsHsQGBtdNHrZONd
 -----END CERTIFICATE-----
diff --git a/cluster/certs/etcdpeer-dcr01s24.hswaw.net.cert b/cluster/certs/etcdpeer-dcr01s24.hswaw.net.cert
index 98987fd..1f57a83 100644
--- a/cluster/certs/etcdpeer-dcr01s24.hswaw.net.cert
+++ b/cluster/certs/etcdpeer-dcr01s24.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFITCCBAmgAwIBAgIUZZUchWgXTldXCL5R+Ia9khASeHcwDQYJKoZIhvcNAQEL
+MIIFITCCBAmgAwIBAgIUWKSJE21a++cxOE0Z2DJosY76dDEwDQYJKoZIhvcNAQEL
 BQAwfTELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMQ8wDQYDVQQH
 EwZXYXJzYXcxGzAZBgNVBAoTEldhcnNhdyBIYWNrZXJzcGFjZTETMBEGA1UECxMK
-Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTE5MTAzMTE1MTkw
-MFoXDTIwMTAzMDE1MTkwMFowdjELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
+Y2x1c3RlcmNmZzEVMBMGA1UEAxMMZXRjZCBwZWVyIGNhMB4XDTIwMTAwMzE1Mzgw
+MFoXDTIxMTAwMzE1MzgwMFowdjELMAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93
 aWVja2llMQ8wDQYDVQQHEwZXYXJzYXcxIzAhBgNVBAsTGm5vZGUgZXRjZCBwZWVy
 IGNlcnRpZmljYXRlMRswGQYDVQQDExJkY3IwMXMyNC5oc3dhdy5uZXQwggIiMA0G
 CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDYaeLRNwXmdRabgLr4AxRpEHOfMtru
@@ -21,10 +21,10 @@
 BggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUI6hU
 9NpYy72mFYPrD2Rs8pEFR6cwHwYDVR0jBBgwFoAULXuar2iqGaTMKoi++HbkvSen
 JA4wHQYDVR0RBBYwFIISZGNyMDFzMjQuaHN3YXcubmV0MA0GCSqGSIb3DQEBCwUA
-A4IBAQAct/qxRCQQ7N9y6sgs7dnXyKhFVHDVMZKvCn5xtDQASTUZwMbVy6rVt74j
-5xX+dHwxgRwvDMP8V5TeVCaud2TlJbwsp9WkfD9wDPFIBmL/aQB6Ksq9hCdj/Is8
-zMLypHF9v9oP7jRy7OD/lyfsGpRESF5UVYKjlZZCIz9K9N/CT+GBPGn80CJ9D3MG
-MOCcu3FyzLjrZwFBCDghXXT0RtnmwRwGodsfeOHA2v4OU2J1HN2VyDIXKlOEYd/l
-D0tQsNR15s3RrlkqgnfRkCi/b3iRlsvNuLX8YbQTJDy+HnpFRKHp36qPDIoCXDQ9
-GreAdy9AaRpEpAIOt221OXX9qc+i
+A4IBAQA27E7NsihLZRJeoeNRA3EGLT1loZzYyCKZZ+uF2nrdVLXfDeHMusfJRacW
+XzyiOveNm2vc5EqZY7/yuALZMZqN75+Zm1/9xbWql9vPOD++5TaY3y9ethiCKzmw
+momYt06iR3qrEA5SN/uKt1+1ikRGGkT9SvWSu86tIHf5P2GP2cb//vGeFlygqZxN
+QCWaDe8sM9J4rozHwTS/shkiifMgkbcRNlAy3K6M9zC9DWoM1xe1mF/pSD/DoMUG
+19Mp52QREETHe+Cfj+wlXFWPqCg1RAEcHMdNfWdCkwY30VVy3OQgSEIgSdRZIlWG
+eWEAsxj9XldOG7/9hxl9RHnQHl6U
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-apiserver.cert b/cluster/certs/kube-apiserver.cert
index 76e25a8..ca8df8c 100644
--- a/cluster/certs/kube-apiserver.cert
+++ b/cluster/certs/kube-apiserver.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFOzCCBCOgAwIBAgIUFeoDxwVWSi1SvKTE4KnOIqE73ZkwDQYJKoZIhvcNAQEL
+MIIFOzCCBCOgAwIBAgIULKpBQwMbte2UAxQf/JOymdUwqFkwDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTEw
-MzEwMDUwMDBaFw0yMDEwMzAwMDUwMDBaMGQxCzAJBgNVBAYTAlBMMRQwEgYDVQQI
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDEw
+MDMxNTI2MDBaFw0yMTEwMDMxNTI2MDBaMGQxCzAJBgNVBAYTAlBMMRQwEgYDVQQI
 EwtNYXpvd2llY2tpZTEPMA0GA1UEBxMGV2Fyc2F3MRcwFQYDVQQLEw5LdWJlcm5l
 dGVzIEFQSTEVMBMGA1UEAxMMazAuaHN3YXcubmV0MIICIjANBgkqhkiG9w0BAQEF
 AAOCAg8AMIICCgKCAgEAsx1dks+6hu3dWLizbUf5egqRfax9oaKJn7H2+F0ndoIX
@@ -21,10 +21,10 @@
 CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKBFe5T0rvllN4ZQuSFH
 UGOcke7SMB8GA1UdIwQYMBaAFJgyXQ5PMx77CemJJBMWQmqA0ZnQMEIGA1UdEQQ7
 MDmCDGswLmhzd2F3Lm5ldIIja3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5rMC5oc3dh
-dy5uZXSHBAoKDAEwDQYJKoZIhvcNAQELBQADggEBAA3tA/YcvwW0HMDLHiD1jSHW
-7LgzF7UjTEa2yfGy7sxR8yX/L0DO6vm6nPdQKzKt/7eDMpMRDJyuJljP5kGfNYgG
-UA34uoMTP98AKusCfv+xsA+V6NVanI0D4lLfX4FiCZXnjX1ZpbiOOZ2Sb0s4U/VY
-SG0v8hoWeMSBE1IlrClJuT2PCfh/TBUFErqPboDWIMlhc4Grbdno28KsBS0daeKY
-fndNhGTfRW03Kse1b0Z3/3u+igjbjVOHE010n9gFNySB5Eu0SRIxIKct/1b0Pwzw
-DoiL9QmuHH9UPDYdWeryJC6mxy21AhNWVRBJVDy5Przi9W+3yhyZsgv21sB9Ceo=
+dy5uZXSHBAoKDAEwDQYJKoZIhvcNAQELBQADggEBAK4JvvqER6qUuKIuAu/b+Zz1
+0B4qLePwgRgeanMPL6/sdwUT1MSWzhgOua4g5NeNPjZduFe6SDT+26/OSFJ4+oYd
++keYEQuzjR+Dv/V9zkgNxX7npplMg6LdrE1qJh6v62tJhy8z+aEh1cC9cLvP0M3a
+yhKbwLT/fl2q6BlS1cB7I6kilU/8D15LBJjqMo9jCmdmY0cxEQtLBs5UgRuMHf3G
+hISDdjoRCQuUM1VC89oiu8nAqgyzxUbVJtJwdRoxMlSHvzY60Uu9CwSTCUTYIlnW
+SVHsfhjRjR37gM+MXMrxgJG+hDU+67nVoAuA05CUrY6qwFKKyGK3smPCFw/TdAc=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-controllermanager.cert b/cluster/certs/kube-controllermanager.cert
index 9e834f2..291c604 100644
--- a/cluster/certs/kube-controllermanager.cert
+++ b/cluster/certs/kube-controllermanager.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFdjCCBF6gAwIBAgIUcEMCdXAJRvlOgGKso6iXGykspCQwDQYJKoZIhvcNAQEL
+MIIFdjCCBF6gAwIBAgIUFcalvELfl2XL6uFIqLjD+5G2tD4wDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA0
-MDYyMDQ4MDBaFw0yMDA0MDUyMDQ4MDBaMIG3MQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDAz
+MjgxNTE1MDBaFw0yMTAzMjgxNTE1MDBaMIG3MQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEnMCUGA1UEChMec3lzdGVt
 Omt1YmUtY29udHJvbGxlci1tYW5hZ2VyMS8wLQYDVQQLEyZLdWJlcm5ldGVzIENv
 bXBvbmVudCBjb250cm9sbGVybWFuYWdlcjEnMCUGA1UEAxMec3lzdGVtOmt1YmUt
@@ -21,12 +21,12 @@
 pTfb5fo5ZuSyY1fSjq2f8LzCOeSmIyb0HKTVvWa1vo3yiXECAwEAAaOBqzCBqDAO
 BgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwG
 A1UdEwEB/wQCMAAwHQYDVR0OBBYEFAjGzAQ6wd1qf2er14tBEmwSV76JMB8GA1Ud
-IwQYMBaAFJgyXQ5PMx77CemJJBMWQmqA0ZnQMCkGA1UdEQQiMCCCHnN5c3RlbTpr
-dWJlLWNvbnRyb2xsZXItbWFuYWdlcjANBgkqhkiG9w0BAQsFAAOCAQEAQWgcjWP2
-nYF1HLYMZzkfbHaBcCCcPH1WBMBJXCgAHfdREwtj/Y2ouDB7XGcBnq7uwmHdfc72
-TLsduzlSfYIyvefUBa+xRBoZ9jtte7oesryb4VvrtiEgKxeOODjFb8fYgxOphmDK
-/FO7iGO4Fk6hB1P6Tk5+2OkEgJV8UEkIO2oncFCQa4fKnz2KiDjguZtAbXRcAou+
-MpVw0jRKXm0Ih/IFjOxF7tm/rXmk5FQ4aTaoLfBD5WtWVnX2BQmX7VMm8HY/vNfq
-EhYu4qqKxS6u92cRxXXeNHDLex+LMv7/3X/SB+gpd9foeAbPmPZKi/3Awi9u7X48
-jZNAXYMAEMg7sQ==
+IwQYMBaAFJgyXQ5PMx77CemJJBMWQmqA0ZnQMCkGA1UdEQQiMCCGHnN5c3RlbTpr
+dWJlLWNvbnRyb2xsZXItbWFuYWdlcjANBgkqhkiG9w0BAQsFAAOCAQEAjeeYwCxm
+8yfTfiWSKMMW9HTlK7zAl8PKngOvARihFgUfO1MbWQLTqYvkZ8/b8d3tXqxGHIY8
+TqLtK0N4a3ty+7IFwqnA29+apSPQOjK2f6RfSwUPFLqGDFXcM0pHHRbalYmUM///
+pEu3B223yimrtacAj3NFy1c5Jd7V36ZhwBAzzh3raWpqbuvm80MZccmb1gml1a/G
+KuyLzrtu8N6ObpsMFQSDbelgvgVvNh0akPANqrDc8BG5gDWkurIhsRWQC+POMKlE
+/c/EgyGXpZfvLtTprgCz+RRIGhV3GNRRbN/sdc3jO7M9QyJod3639VCf5gg5EU2l
+5sbxhHL+mvqJwA==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-kubelet-bc01n01.hswaw.net.cert b/cluster/certs/kube-kubelet-bc01n01.hswaw.net.cert
index 94b95df..d846aa9 100644
--- a/cluster/certs/kube-kubelet-bc01n01.hswaw.net.cert
+++ b/cluster/certs/kube-kubelet-bc01n01.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFVjCCBD6gAwIBAgIUPmXvbmeRs74W9l5NLg6qG8QKJ90wDQYJKoZIhvcNAQEL
+MIIFVjCCBD6gAwIBAgIUfBwlWrk6L56SB1jAWUjjt4c5rOUwDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA0
-MDYyMDMwMDBaFw0yMDA0MDUyMDMwMDBaMIGFMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDAz
+MjgxNTUzMDBaFw0yMTAzMjgxNTUzMDBaMIGFMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEVMBMGA1UEChMMc3lzdGVt
 Om5vZGVzMRAwDgYDVQQLEwdLdWJlbGV0MSYwJAYDVQQDEx1zeXN0ZW06bm9kZTpi
 YzAxbjAxLmhzd2F3Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
@@ -20,12 +20,12 @@
 QaaisNvK2CqFoim0cjiBWWTlpUxv3XF0TCnlso18z5EVAgMBAAGjgb0wgbowDgYD
 VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
 HRMBAf8EAjAAMB0GA1UdDgQWBBRQZwM3WgGW+l8MKQBF2DZ5IWBh6jAfBgNVHSME
-GDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ0DA7BgNVHREENDAygh1zeXN0ZW06bm9k
-ZTpiYzAxbjAxLmhzd2F3Lm5ldIIRYmMwMW4wMS5oc3dhdy5uZXQwDQYJKoZIhvcN
-AQELBQADggEBALQ9uq5DCJYilyUQ1HYT1pP0PD8szSscTsQCVA5ExEuevlTn4ka/
-qtru+4Ht9eap12cmHqEQFpVZpBQyLmgRSVZPALNVYmaCrATyskz3uKDWUtRM1yAF
-+CfSR9Ibi6l9U4FOoA8U2xDrOAzJN2WYpmv/W363TJt0HuvpbrXEUsN6GFc0c/Sq
-7h/UWzyskoBup8eJrR6WX79pQSfoNCXJrEmGGZ2+hoU1/tF6siWMhwAu1UTpKQCw
-/rcnKwc04WRGl2zk84cffFnmpjJXf2BFdItVDBD5N/e4oRoaNocZjkHnZ/Xg1LoJ
-jC7BMy4ScK4S5Nxem8PVYnpLS7o5KLqqTnI=
+GDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ0DA7BgNVHREENDAyghFiYzAxbjAxLmhz
+d2F3Lm5ldIYdc3lzdGVtOm5vZGU6YmMwMW4wMS5oc3dhdy5uZXQwDQYJKoZIhvcN
+AQELBQADggEBAKfYV2qyVs9yvsOhjKo8/A/t8Juz6FCBbPuuTdUt0TVENro3/njr
+wvl+TrtdvRwOfgYbzl+UUKgmwOY9gBumWeEOHeOUSJS+Cz9Ad5YgrQzQ9T9stOAC
+/8DdGq4rMaU2tbxpNCAc38XJUmwOnSrVbreWc98LZSlV17FFoB45R7FFhz0VZHpM
+B0/I+fK/IkF3CbSHVViGqjPcTwASU4AQaw8mcEAvpT4yXF7nUY+jORZSH/48y6U4
+6hApMGjLrdnSyCPddvPxfW6BFw15TNh2PnCNIb5P0fwitjVcArtock7T/vHVe+8Y
++Xc7y0kqHBNKNDFRaA5I4HIF+Jc+kzZgeaA=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-kubelet-bc01n02.hswaw.net.cert b/cluster/certs/kube-kubelet-bc01n02.hswaw.net.cert
index 835386d..919dcc2 100644
--- a/cluster/certs/kube-kubelet-bc01n02.hswaw.net.cert
+++ b/cluster/certs/kube-kubelet-bc01n02.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFVjCCBD6gAwIBAgIUd3WmIRGYLAcILkm11fqlgkalJEowDQYJKoZIhvcNAQEL
+MIIFVjCCBD6gAwIBAgIUXvb9hqtoTXFM458nQblwXTSNeLAwDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA0
-MDYyMDMyMDBaFw0yMDA0MDUyMDMyMDBaMIGFMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDAz
+MjgxNjQ1MDBaFw0yMTAzMjgxNjQ1MDBaMIGFMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEVMBMGA1UEChMMc3lzdGVt
 Om5vZGVzMRAwDgYDVQQLEwdLdWJlbGV0MSYwJAYDVQQDEx1zeXN0ZW06bm9kZTpi
 YzAxbjAyLmhzd2F3Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
@@ -20,12 +20,12 @@
 GJzRDqOv3q3hGo7+a6gVMU60DHsgTFbTKJ1TUh0V+D1nAgMBAAGjgb0wgbowDgYD
 VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
 HRMBAf8EAjAAMB0GA1UdDgQWBBSnIgfLJiK7R+k9wfSCeKuqjpkYNjAfBgNVHSME
-GDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ0DA7BgNVHREENDAygh1zeXN0ZW06bm9k
-ZTpiYzAxbjAyLmhzd2F3Lm5ldIIRYmMwMW4wMi5oc3dhdy5uZXQwDQYJKoZIhvcN
-AQELBQADggEBAEV3RzyxUHspOi5ZX3p2y66dJaRpF2ja8EUgXZHZ9ls+IsuKkxBe
-2pSfo9rWAJu10h05UztN8ruL1+OuVitUYWPvhr3XpdmxfklGgU6yfGjfb9HeBAC8
-qfoeLZ9T59qiYfGTmm/KO8C2BGynd/VeWpRNrCcREdCyxP8v97oSqS88qY6GHAn/
-ijnbnTSEVc65P/YS7CayAoXzBFgtmcvwE0E9JxuJ9RlD3TZQd4Vo77V++QKGPr34
-Z2inu7WSGou1tsuue/fyuuDHHm82ZdPYjYLM6/HHkx5v7t0e+EM1ghc8qVbrpBdi
-dzgdOByF7drN8eQurjOgSCh0BzNLMdXm8Rc=
+GDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ0DA7BgNVHREENDAyghFiYzAxbjAyLmhz
+d2F3Lm5ldIYdc3lzdGVtOm5vZGU6YmMwMW4wMi5oc3dhdy5uZXQwDQYJKoZIhvcN
+AQELBQADggEBAJ0HFPiLL+Opy04Zm3H5bRHOlUcPiSrRUi4QM8PnnrC0t9R1Wvlb
+PvuvAG2EI2rQsN9qi73riOW5KwUmvxe3ArpHH20uhUumBfyikK3nqnQW6XNBzirQ
+pv/2b0Pm9CCn71ETcCrUaenGaUjUhmY4Ojvbp4Ycc5LQ2E4PlsR11GnETM15CK7K
+0z1VUtiu1+XubS+1trYw5aUF3WQGitTDl4T8VCdQRUKeyygO1HMQwmJmRwuMMLrP
+MTbaNOQBD+c+QIzQDE/+yGPkItU2efBmNvsp6B54AHznEkUMrqJWzkt4SnJ2KbAu
+DtzSfnNpaGpluvOyh0NxHsCcUMlm9J8ajNk=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-kubelet-bc01n03.hswaw.net.cert b/cluster/certs/kube-kubelet-bc01n03.hswaw.net.cert
index 3939527..52a01c0 100644
--- a/cluster/certs/kube-kubelet-bc01n03.hswaw.net.cert
+++ b/cluster/certs/kube-kubelet-bc01n03.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFVjCCBD6gAwIBAgIUXfXM+dY4X8VJWHt/56FVD2BnK4gwDQYJKoZIhvcNAQEL
+MIIFVjCCBD6gAwIBAgIUbS3md+5hxDAJRfmGxv8lV5m21x0wDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA0
-MDYyMDMyMDBaFw0yMDA0MDUyMDMyMDBaMIGFMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDAz
+MjgxNTE1MDBaFw0yMTAzMjgxNTE1MDBaMIGFMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEVMBMGA1UEChMMc3lzdGVt
 Om5vZGVzMRAwDgYDVQQLEwdLdWJlbGV0MSYwJAYDVQQDEx1zeXN0ZW06bm9kZTpi
 YzAxbjAzLmhzd2F3Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
@@ -20,12 +20,12 @@
 H0ShXe8QV2yvt0ISPzJjHNxG+pv79moribfs1gX5KUYpAgMBAAGjgb0wgbowDgYD
 VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
 HRMBAf8EAjAAMB0GA1UdDgQWBBT1m3RgBiqGCDhqwxutcUg0f9nBzDAfBgNVHSME
-GDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ0DA7BgNVHREENDAygh1zeXN0ZW06bm9k
-ZTpiYzAxbjAzLmhzd2F3Lm5ldIIRYmMwMW4wMy5oc3dhdy5uZXQwDQYJKoZIhvcN
-AQELBQADggEBABW6nR/wWc5SRJH+AnkK+YaqL0baO6BtKzNHSLMIHDDDfSufeNUx
-uAMy9j7Or44++9b6jS6nE0cWWMk8BSL1YZ9fTSP6vl9FIDdA9Iezdj92lcC94y08
-pmkI8Zdlk9Jc5NklU4a1gnipWpYU82RfHvoFREotSkn/u3Fp7DmaoWhzDwcRIiiP
-+wDeMf9BWt/uVvMo8dCKImWannPI+LNdrqRovNF0sQPxsFAgWby4r/TYPmPbg1E4
-MaGVuvk/dyHw6ijs+Bgy3DQ2virMfEl4UiJ/lTQUra0uVWydMobFLaQXJleQpjC6
-NGAxyBrbIuXCFu9fMppOQrtj2BLBhc3VO/k=
+GDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ0DA7BgNVHREENDAyghFiYzAxbjAzLmhz
+d2F3Lm5ldIYdc3lzdGVtOm5vZGU6YmMwMW4wMy5oc3dhdy5uZXQwDQYJKoZIhvcN
+AQELBQADggEBAFmt/mBuQHW0mfWNgc91OhNRUAL4Y23zFy1hpL4t0VNtGwEv51K1
+hTV7GlQHAcjE0Ti8Ivb9b+gU0FV6E1xFDsXg3w/unmZBhFMnKkwR/f8AIadgO/JT
+MgV4XvQgxXwVRetetXbr2uQV4Nz5cji9E2Rcad6NkN67FNpKratKR0+sPWCz9DYJ
+5mPlfmGBBW6ptAMGnekg0ttvup1a2FbCCxKpMnL+X4hv0a05Pgviwemm+uwckl/k
+zTqB7VDYtlS5SloRpHP4D3VxXU6j2vwkV7D/pEWKY5kXTHmBN2VDL+9mU03LB+aa
+NAeu2cme5Cu80BfzG+Eit7AD6hapm1WLmuM=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-kubelet-dcr01s22.hswaw.net.cert b/cluster/certs/kube-kubelet-dcr01s22.hswaw.net.cert
index 0e8c29f..5ebff9a 100644
--- a/cluster/certs/kube-kubelet-dcr01s22.hswaw.net.cert
+++ b/cluster/certs/kube-kubelet-dcr01s22.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFWTCCBEGgAwIBAgIUMajhf+ol+qYrPkWO4cUmxAzERiIwDQYJKoZIhvcNAQEL
+MIIFWTCCBEGgAwIBAgIUNiys02uLuZgxUVPAVK3RS0AcKTYwDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTEw
-MzAyMzAxMDBaFw0yMDEwMjkyMzAxMDBaMIGGMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDEw
+MDMxNTMzMDBaFw0yMTEwMDMxNTMzMDBaMIGGMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEVMBMGA1UEChMMc3lzdGVt
 Om5vZGVzMRAwDgYDVQQLEwdLdWJlbGV0MScwJQYDVQQDEx5zeXN0ZW06bm9kZTpk
 Y3IwMXMyMi5oc3dhdy5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
@@ -22,10 +22,10 @@
 VR0TAQH/BAIwADAdBgNVHQ4EFgQUW/G38M+taLCcnLVTk/lfdpQDbrUwHwYDVR0j
 BBgwFoAUmDJdDk8zHvsJ6YkkExZCaoDRmdAwPQYDVR0RBDYwNIISZGNyMDFzMjIu
 aHN3YXcubmV0hh5zeXN0ZW06bm9kZTpkY3IwMXMyMi5oc3dhdy5uZXQwDQYJKoZI
-hvcNAQELBQADggEBAEMzvEAEpg/rJb03iDvfPXElD/YBezoOGhDP7aRWwnJh0NtW
-/gx4CvrTq922EmM3ZquuFDIFABt+cr09KZWDC7HsYQyxZuOZK+l08PWG1hW3/2T9
-tpJM274LEwQJE+rKFlq22YFg/b844DSzCvIC2EQZaSpaBt5UY1685sO84rG0YtCz
-5uqGoYU5A/9q3anxIQh3nP6KhlEDruMNxBaZRwm/MyB3J3PafqfbXRgZH6ESi2h6
-Y1E3hlkwkrBBqCfVOUbWs4echvPvL4ysPi2FNzo4L0eXYn7+Xiv0tfAoIhRgRMeY
-qY6wgjdRMORwXSEPfiUqOhTz2EUctXR7MQu3sfg=
+hvcNAQELBQADggEBAGgM2YokHvqZdziBEfzOTSxbmrUuGRIPsvL0uqq5N/sm28ti
+FzlrfwWO+HEVkRA6fMKpwA2nAn+JGg/PcTgH3UrNfSTDQIMUqEAQqbtwdtnEL/ND
+AXAs06wnnDZ3IvyQZAyGKDEzbmyjroTfVWtssXtUhMXDH+j/lm3Ga03QhqzbO9zS
+RloCTM8bBxNpPS6aNlT8GVF4jv/u/REuEtbRPrYUpjVub9QpDJxXcdLXd6Hmz9zR
+32j5TEY2s0ub8duc+y6Xaljkl2fnezUk71QaKxgYWRCuC8eF4ZGWrKsfP0Q1IdZX
+OaDf8ePgtVcOFBtLy2foJriECRbXX2/r96kwAGU=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-kubelet-dcr01s24.hswaw.net.cert b/cluster/certs/kube-kubelet-dcr01s24.hswaw.net.cert
index 353f4f1..38286fe 100644
--- a/cluster/certs/kube-kubelet-dcr01s24.hswaw.net.cert
+++ b/cluster/certs/kube-kubelet-dcr01s24.hswaw.net.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFWTCCBEGgAwIBAgIUHPS7inpM8ExeIKErCehvZiwDUNgwDQYJKoZIhvcNAQEL
+MIIFWTCCBEGgAwIBAgIUD02W4qrbKIQLaBbi/rph4q6DkLQwDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTEw
-MzExNTE5MDBaFw0yMDEwMzAxNTE5MDBaMIGGMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDEw
+MDMxNTM4MDBaFw0yMTEwMDMxNTM4MDBaMIGGMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEVMBMGA1UEChMMc3lzdGVt
 Om5vZGVzMRAwDgYDVQQLEwdLdWJlbGV0MScwJQYDVQQDEx5zeXN0ZW06bm9kZTpk
 Y3IwMXMyNC5oc3dhdy5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
@@ -22,10 +22,10 @@
 VR0TAQH/BAIwADAdBgNVHQ4EFgQU98WJLNafrXaajcrVAVuoPlj3EhcwHwYDVR0j
 BBgwFoAUmDJdDk8zHvsJ6YkkExZCaoDRmdAwPQYDVR0RBDYwNIISZGNyMDFzMjQu
 aHN3YXcubmV0hh5zeXN0ZW06bm9kZTpkY3IwMXMyNC5oc3dhdy5uZXQwDQYJKoZI
-hvcNAQELBQADggEBAEiUHf5ZVv2R/jyDI25ctQQp7p9UqKygHKeWOPnvJKIRaQtl
-1PcVIuu0gfpvWgPQSPLUJV8GIN3zJCZLDyHeXZaer4Bo5ivRIX9a5Q+Ino8xLaoX
-6lxkvtBEipdZJ9Gc8GbXo80EinefUnMOlEmXKKC/KWSb9qK2VAbXZam8GGntN4np
-GDDBDbM/Frsz+o+xsLTiozO1gBsJYkl5M3K7vtpl16+hv/QqlK+mnE5ghEfxi6Ss
-hVAf/I9Zg4e45g3nVQwqKhw5nekad/lsnvN/hpuKZhZQdCudUOuuwbW1sc3540Ff
-L77u3ZR/jexJrxR4wzplKl1wtSxJ74x2LaOAIS8=
+hvcNAQELBQADggEBALb8pvxAMWf9CHyDmfj/iXwkDRqHCgNu+qaEzSp4foHgh5ch
+QOs416s8bLyCqIXhoL/vZGCnIRAHBuYunwbofVJnEqo27nLo0o2rH/ABSxCpukmA
+WEGrTv/0wp98gjTNNMX590ws71XrsLRTig4dOTch6NhxoJz5VMOY7vPIItiyFLe2
+Y/LutQA9Wwz3ki6hPs69SJcrLbmfzKOEpJNMKIHhHcwDqT2weTOiZOP7/CsoGPje
+v4dylmcJTMTq0YIQmTDRlKbSGPbE4uVhizytvb2CwODQb+taqQwQ4aRdT76HAdYK
+YbX163CxALNYVYQNkB6dhUR5JNbYnAUwon4h18U=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-proxy.cert b/cluster/certs/kube-proxy.cert
index fe99d01..c638345 100644
--- a/cluster/certs/kube-proxy.cert
+++ b/cluster/certs/kube-proxy.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFQzCCBCugAwIBAgIUZlYtttc6/gOrhyj8uQTG7hFz3powDQYJKoZIhvcNAQEL
+MIIFQzCCBCugAwIBAgIUNOcUBxeoeF2k8lsdrL+mqCe0O7swDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA0
-MDYyMDMwMDBaFw0yMDA0MDUyMDMwMDBaMIGRMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDAz
+MjgxNTE1MDBaFw0yMTAzMjgxNTE1MDBaMIGRMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEaMBgGA1UEChMRc3lzdGVt
 Omt1YmUtcHJveHkxIzAhBgNVBAsTGkt1YmVybmV0ZXMgQ29tcG9uZW50IHByb3h5
 MRowGAYDVQQDExFzeXN0ZW06a3ViZS1wcm94eTCCAiIwDQYJKoZIhvcNAQEBBQAD
@@ -21,11 +21,11 @@
 AAGjgZ4wgZswDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
 BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTn0MVsvuVE2ZiSJNZfEp6Q
 pb8u9jAfBgNVHSMEGDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ0DAcBgNVHREEFTAT
-ghFzeXN0ZW06a3ViZS1wcm94eTANBgkqhkiG9w0BAQsFAAOCAQEAEsBVXPHfGVdL
-s2BDmbhArb98byrWGbSWyz008OaDt6LLrneUDIyiwJgBOxgyY5vPR9fz5qSJb3Ua
-/7NngHE4k3afKU8/OI/mrDHIwnHrKuKWNpYcpotzKbHhTBn0erptl+KJIGhiUgOW
-LTSvEG/0k5Kxrs737Eq9R0DsOe2vNiw+IerNUAyG0wwD+HbT6pEkE6gsD6k8Fkwc
-kO+JT2hs/e0bcaCb4PUMV8CMqe5sZKGOcr1foUP72GOpE7oZ4Madq2AuNZnm4RIo
-xJGAVfejo3JG5qWglk8Kl1qGl0Wn2yUqRp3ErMUY/7UFJSKazucfnZ3zic1Z4pB4
-3svHQJ+pdA==
+hhFzeXN0ZW06a3ViZS1wcm94eTANBgkqhkiG9w0BAQsFAAOCAQEAa+LkfAbWHUSs
+19veJ09P0uo6PYKqXsOrh9soWX8lusI3Zt3zhTdSXkzJwKi8bH3zFx8niWAIHNwZ
+mxesvJIH6fPA0/401MkjhSRo3cyMUnjKmjx5+DD2qIEKKBPsr/xNMpJGPKaDjGtk
+YyRHW8Bg7kX+Jc/uv7Gg6U+/xtdbELaxL/USufRN7obC7gNtenXkdOUgINrfllX/
+66+K7yqaAqaCR4gEGSLUnUvkbFZ/+XB7Z1tLKgWurJj5v82ZxnkBI+aU3tVwLtoT
+tnH7OLi5Tbo+RYuf3iMd1vGxVwEPcD9cBUz0lRsK9TTJxRytS8CnS3EIwcitYo86
++yl7LltiSA==
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-scheduler.cert b/cluster/certs/kube-scheduler.cert
index 1544599..abdf6a4 100644
--- a/cluster/certs/kube-scheduler.cert
+++ b/cluster/certs/kube-scheduler.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFUzCCBDugAwIBAgIUS4QEUvDV3mIJIIuyi1HJ/lmoINkwDQYJKoZIhvcNAQEL
+MIIFUzCCBDugAwIBAgIUSlMSojfxDUiCtKO/7Mr5kJbxBxQwDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA0
-MDYyMDMwMDBaFw0yMDA0MDUyMDMwMDBaMIGdMQswCQYDVQQGEwJQTDEUMBIGA1UE
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDAz
+MjgxNTE1MDBaFw0yMTAzMjgxNTE1MDBaMIGdMQswCQYDVQQGEwJQTDEUMBIGA1UE
 CBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEeMBwGA1UEChMVc3lzdGVt
 Omt1YmUtc2NoZWR1bGVyMScwJQYDVQQLEx5LdWJlcm5ldGVzIENvbXBvbmVudCBz
 Y2hlZHVsZXIxHjAcBgNVBAMTFXN5c3RlbTprdWJlLXNjaGVkdWxlcjCCAiIwDQYJ
@@ -21,11 +21,11 @@
 pY0nOn+0jIjzAgMBAAGjgaIwgZ8wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
 CCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQw8PPX
 ExubjWf7o/eChtV5jnt1kTAfBgNVHSMEGDAWgBSYMl0OTzMe+wnpiSQTFkJqgNGZ
-0DAgBgNVHREEGTAXghVzeXN0ZW06a3ViZS1zY2hlZHVsZXIwDQYJKoZIhvcNAQEL
-BQADggEBALU1R9svNLyFNCOXcyvAg0T9u3mH5SD0F1MQgKrgZTuVKc4/Oa/LOBbD
-wgwp/1eJZ0xkMVTZl3lw6N6KkgtydbZskc2m2qQPBVdv/RFzecKROI2UKvLL+lTS
-HlNIxv6e5T1q2B52o++B4QoEfBhwclxtq0oHPpqu+7ZQ0lGDHeOcIphyMGONOWoT
-s7LzYMB+ud9XTzdB91eIIXcYZz0OlF5qI21URy/Mi6j1RENG8U+GtGmNnkZ3z3Yb
-SAL8hqDlwabD3V44xqKJMfaWzAbXO43t2FTmyi1uUVsfJc3J/fCSZLJNRfyUOMVb
-mJsokVUNYaaOLrHHZz77+k7ruR/KWkM=
+0DAgBgNVHREEGTAXhhVzeXN0ZW06a3ViZS1zY2hlZHVsZXIwDQYJKoZIhvcNAQEL
+BQADggEBABw3aqj8FQtaZKHHzGY+cpjvOT1VUKax1k0iQAbYS5/8d3kaToDed05M
+omXDcIxb3VHs6+sWxJYWAiRPiA5mrDdA7XQcfIv1xtP+DL3dbRqhz276XNM4/NIj
+vt9aQox/WSE0HCDTUSN/clYbB6tigLfSxXhnuz214N6NwkcTl8xQVvXxg3z6ryc7
+XUTEA0fvl/fe+KsO2l4kxBk9Ef5cud3j2e4F4l8tFHz1bRXfcEEcS5uLLgK3KIAu
+sf3Sf+t/jcTTrJ+3YVFBAY+F7AN4UjNdlAfyvTG7xB+pxD7RlEd6Ycozd0tYppg4
+VBbXtQ4TOxHLVvrlANi3MJAzYSUuB54=
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kube-serviceaccounts.cert b/cluster/certs/kube-serviceaccounts.cert
index 684cce9..7ba5a5f 100644
--- a/cluster/certs/kube-serviceaccounts.cert
+++ b/cluster/certs/kube-serviceaccounts.cert
@@ -1,30 +1,30 @@
 -----BEGIN CERTIFICATE-----
-MIIFKjCCBBKgAwIBAgIUGx40+NyFMYjk3UltyhqHHSfyEkkwDQYJKoZIhvcNAQEL
+MIIFKjCCBBKgAwIBAgIUC8G46cdD+fUIfepfl2RRtz7D5FgwDQYJKoZIhvcNAQEL
 BQAwgYMxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
-CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0xOTA0
-MDYyMDMwMDBaFw0yMDA0MDUyMDMwMDBaMHsxCzAJBgNVBAYTAlBMMRQwEgYDVQQI
+CmNsdXN0ZXJjZmcxGzAZBgNVBAMTEmt1YmVybmV0ZXMgbWFpbiBDQTAeFw0yMDAz
+MjgxNTE1MDBaFw0yMTAzMjgxNTE1MDBaMHsxCzAJBgNVBAYTAlBMMRQwEgYDVQQI
 EwtNYXpvd2llY2tpZTEPMA0GA1UEBxMGV2Fyc2F3MSswKQYDVQQLEyJLdWJlcm5l
 dGVzIFNlcnZpY2UgQWNjb3VudHMgU2lnbmVyMRgwFgYDVQQDEw9zZXJ2aWNlYWNj
-b3VudHMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDS8y3HM9/PG1K1
-9usXmY3dw7x0NEBH2rHJZET8kwnjifU0uuhQwSAwyqp1RuWWUxrIudgPfZ+TrPFA
-fMNpRnEqzbfPYYboXSmK3Yrc+XXJeD+HvD4UKcR2fL0/muNi9qkLeNhZUuLxrXUQ
-JxZhLokqdLo92yy4sjVCNrqJmqyU78I0XCduFwoxQXW8o6v0kIWZj8SsBkr8SMl9
-1drTG86buI31xf+vl9UWfXhChXQ3tHM5LS2mygHZBW7fGTzxydJxkE9g3rw2Y/lD
-ZnHgHdChV1BL2guYudMmlpYcoAL9womhlvmoMTW2BJb0EoBeQRAy1RgohgelusZm
-wTJeXsmgd2vRIbju6u6Qup2r5p8WIpiDPZVC0XZAacO4/NRsC2cYjsXHgdaid73N
-J1wYcauc+ddha07QSwhwcHtKfv+x8QAVgJ38iJvVK+y+iOJctEbx4UBN5jvwxwwW
-rUnSkXpLL+w4+9q3e/FhBdN7HX9luuiZnCMx/nfUDz29461WSYlx2l5HNatHDugC
-vgBJDnQ/1Aj1BNL7dXGsGuO5jjyAfoKvxOVf03rVTLzLhuN5GhC6liCzJJ0Kfm1w
-EwQgpwFZteIvGvLUCg6WF9nwzZqQ9ZNZaOE3wwFkaPA4bJkOs/S+ZDkTFKl+/wVo
-hF36Rnp6zKI1NT2Rz5bo0ZGnc7KflQIDAQABo4GcMIGZMA4GA1UdDwEB/wQEAwIF
+b3VudHMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDFSCquXVjQANUN
+IIkGFgsgrCKxqF4gT1sxIcDnsoyncEXnsdqfYAn3yvi0iEZq0JwMkAhWI9k9oAV4
+DOW3hMBrqWZUjRnHPwUwoewvUwqCZyjOzhFSyy2E2iEq7yfrZkgxVVHIdUMoq179
+/jRRa8fk/oUmqiiQNWy//q6VX1ASX7elh4oKfwRMFwnf6vQO7WUm2wqlbYNHaGji
+XDMVuGUyx8XG/F0c1YrAQPIPx5vU6GVV+Qpdl38E/wDIUCS/RPml8M2Q5eBljo2P
+Xhr26tO2OQuOu5UBvzg4e7k1rEKsMlwQSATB2PIVyLNQrWN6zUuI2pV2OUE6Oreh
+ZI6qpZ3eo+QJi496QriZeZ6tLnzoPPaw9QIJG03Si25PjT7p1ULEx7EQ2OOcBBXj
+UoQF1KDkqoqJ5GEqA2ie/U9FhobFUaQpqiZsOWYG08u9oERzNnK+h057XjLsblod
+Bi4d2x+oLFi0q2V/zb6yts3jHicTEyAXCkOq3q6pFd7N8YUbSU5Og8Bgk6KzPoSb
+Klg6L8ttDwXXQRNl4/1CR6+17hFCECoKRVKvTeOX0O7Rl/raWpL5WmBZohpaDfQf
+VpRBONC4p9K73bnlK7P1E38DrrWO4kO7xrmGF0KuRXVCzBZngG+8dpHJMBg7CyH0
+Wv3ZmrcEgb0rRwWYj8LY71EQzO2D2QIDAQABo4GcMIGZMA4GA1UdDwEB/wQEAwIF
 oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAd
-BgNVHQ4EFgQUbrez0M0eXWQD6wus9hshP8quFBQwHwYDVR0jBBgwFoAUmDJdDk8z
+BgNVHQ4EFgQUliCshdOww6BLgNw1Cu+0XiqCpg8wHwYDVR0jBBgwFoAUmDJdDk8z
 HvsJ6YkkExZCaoDRmdAwGgYDVR0RBBMwEYIPc2VydmljZWFjY291bnRzMA0GCSqG
-SIb3DQEBCwUAA4IBAQAYay9hJEEyj0wBE25FayKEKKT4YM6UiPE0GqOxdyC4J1iv
-GTeb2KpZ/TDnS9oz8+ihyU7IvrtqeDb84rR6Wbb1ae7CaqCPHgrE5CW+O8+jd9TH
-6ZEKcOUDfXWH+2SjQYdFM3NnxXrcdkkgZsy0lZpRu3YWTurzsZI0j8AXLq1W/vgT
-dDMN6jlfQC4HUPMoaFnZFuJel3lU/pB6E0j7ErQf+c8q2knu+jNUjJmet+l7I4VT
-0YJTpY6oVrK1CZ1XFOnhWUCYCBfKZ/05QzkIv4SsE5HfWEIN1Ne51vsnm8JBEpz/
-8elXu6B8P1V/2AXun9/R0v4zkuDm953885MGHrW2
+SIb3DQEBCwUAA4IBAQB7xM6vfvk3dw9cFP0F2YTAxLVot1E+KzHWz952uIm3CrtU
+Vq3WHBX3NRTVrzg3Ycx4tNniOHBqNrzgksz0XmFZw7VyiY+yEzueVCJ9HU9y8Kb2
+XdL5zqTtgVYspr0dI/34NbGnFVJAOJ57fAc4LxhPwAZMG6s4LwDiBDYIBw+KoJsD
+FOiHJ+AfW5taGONEGY+HuNnSo+RllCgFdjPW0hK4X8Jt4p5Qr+oICO4Nzp4jZwv0
+WqxHzmX4DYDQLztqrQelSDkaQaP/xAhq7nsaK91sMob1OqQcYSMckm2SFEmTwBCs
+VpJg24y/LRw1LPK4lE7GGGIkyko+aDCsIm+YDX7U
 -----END CERTIFICATE-----
diff --git a/cluster/certs/kubefront-apiserver.cert b/cluster/certs/kubefront-apiserver.cert
index c8fe77b..409beb3 100644
--- a/cluster/certs/kubefront-apiserver.cert
+++ b/cluster/certs/kubefront-apiserver.cert
@@ -1,9 +1,9 @@
 -----BEGIN CERTIFICATE-----
-MIIFEzCCA/ugAwIBAgIUBrhuHQZXw8Fe+lov3RlTO4t7kBEwDQYJKoZIhvcNAQEL
+MIIFEzCCA/ugAwIBAgIURk8WW4qapypnrPH9a2aMG+oMUpgwDQYJKoZIhvcNAQEL
 BQAwgYcxCzAJBgNVBAYTAlBMMRQwEgYDVQQIEwtNYXpvd2llY2tpZTEPMA0GA1UE
 BxMGV2Fyc2F3MRswGQYDVQQKExJXYXJzYXcgSGFja2Vyc3BhY2UxEzARBgNVBAsT
 CmNsdXN0ZXJjZmcxHzAdBgNVBAMTFmt1YmVybmV0ZXMgZnJvbnRlbmQgQ0EwHhcN
-MTkwNDA2MjAzMDAwWhcNMjAwNDA1MjAzMDAwWjBmMQswCQYDVQQGEwJQTDEUMBIG
+MjAwMzI4MTUxNTAwWhcNMjEwMzI4MTUxNTAwWjBmMQswCQYDVQQGEwJQTDEUMBIG
 A1UECBMLTWF6b3dpZWNraWUxDzANBgNVBAcTBldhcnNhdzEcMBoGA1UECxMTS3Vi
 ZXJuZXRlcyBGcm9udGVuZDESMBAGA1UEAxMJYXBpc2VydmVyMIICIjANBgkqhkiG
 9w0BAQEFAAOCAg8AMIICCgKCAgEAuVXNUv1oJrLw9XxagSTyHegDeHT71JSdeYWx
@@ -20,11 +20,11 @@
 /xZzYekCAwEAAaOBljCBkzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
 BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKkQFBbQv3W0
 jUMCdTBkmb0YlS7DMB8GA1UdIwQYMBaAFI+XbXEIcmDLzHcqyvkE9KRqvCX0MBQG
-A1UdEQQNMAuCCWFwaXNlcnZlcjANBgkqhkiG9w0BAQsFAAOCAQEAW9DxplTEyKrA
-WM3yC/kkSz9iTSqSO5X3QWFPydvN3nUY2g8CD4f5VYPAZbqljNu2W/dA9LberQl3
-FUh5PM6E1+QekdosHfiQrjBi4mD8G7mVkJBCtrcflerpb6+VhYUlHvMMITbV5wrx
-Hz4/G2Ndym9IqwVBn8Srbjh3w1yYcVUGsQD7HHxJOnE9YknlQH73tMfbyPYsEgUo
-fB5unEBGRrEjqGa2lCWHwxE0MT5WowsKUhKr5ikH3l2AufOnVETfkq24yHi/qKt7
-Lq/303b/5y/2uwC5J0wpiqd71BuwXJoprAMHPjI8b58MKZtMBsYDTBkP8Ls150CP
-PpB6p9/L9w==
+A1UdEQQNMAuCCWFwaXNlcnZlcjANBgkqhkiG9w0BAQsFAAOCAQEAGI6MmkpsH4Ur
+mU20a8EmXkaGK9pD4hvbnsFsnzR0CjE8KHmDWIKlkg6RJZlnZnsd4UmnJTbnpM6A
+IkOycdwXsD8QrWwOMOqT3mIw4OYZqog4WqXcXlpeQwDmzC5mqEPWmy/Vr0CCIzFF
+Cx2ElcoIBQ46o9hm4Lx91uyWqDFRsBLleE+rgr9nCqesG4kYylT4Tb21l+YGQqtC
+v1mpXD7jaoyVVGpm28zUE2v/bGZsBfmd5cC9MVvyVrlhL4soI1UCO5aP/n/DxY0a
+iJ3UvlNnUbFuo0GUalbIwlTlVK/l6o6XRPINGbxTPEaueDLPKIqvvF+ZZBDMx7Hl
+k3IBIbEW8A==
 -----END CERTIFICATE-----
diff --git a/cluster/clustercfg/BUILD b/cluster/clustercfg/BUILD
index eef2049..433f79c 100644
--- a/cluster/clustercfg/BUILD
+++ b/cluster/clustercfg/BUILD
@@ -1,3 +1,5 @@
+load("@pydeps//:requirements.bzl", "requirement")
+
 py_binary(
     name = "clustercfg",
     python_version = "PY3",
@@ -7,7 +9,11 @@
     ],
     visibility = ["//visibility:public"],
     deps = [
-        "@pydeps//fabric",
+        requirement("asn1crypto"),
+        requirement("cffi"),
+        requirement("fabric"),
+        requirement("idna"),
+        requirement("six"),
         "//tools:secretstore_lib",
     ],
 )
diff --git a/cluster/clustercfg/ca.py b/cluster/clustercfg/ca.py
index 9ed2053..0107080 100644
--- a/cluster/clustercfg/ca.py
+++ b/cluster/clustercfg/ca.py
@@ -1,4 +1,5 @@
 # encoding: utf-8
+from datetime import datetime, timezone
 import json
 import logging
 import os
@@ -171,6 +172,35 @@
 
         return key, csr
 
+    def gen_csr(self, key, hosts, o=_std_subj['O'], ou=_std_subj['OU']):
+        """
+        Generate a CSR while already having a private key - for renewals, etc.
+
+        TODO(q3k): this shouldn't be a CA method, but a cert method.
+        """
+        cfg = {
+            "CN": hosts[0],
+            "hosts": hosts,
+            "key": {
+                "algo": "rsa",
+                "size": 4096,
+            },
+            "names": [
+                {
+                    "C": _std_subj["C"],
+                    "ST": _std_subj["ST"],
+                    "L": _std_subj["L"],
+                    "O": o,
+                    "OU": ou,
+                },
+            ],
+        }
+        cfg.update(_ca_config)
+        logger.info("{}: Generating CSR for {}".format(self, hosts))
+        out = self._cfssl_call(['gencsr', '-key', key, '-'], obj=cfg)
+
+        return out['csr']
+
     def sign(self, csr, save=None, profile='client-server'):
         logging.info("{}: Signing CSR".format(self))
         ca = self._cert
@@ -246,12 +276,30 @@
         with open(self.cert_path) as f:
             return f.read()
 
+    @property
+    def cert_expires_soon(self):
+        if not self.cert_exists:
+            return False
+
+        out = self.ca._cfssl_call(['certinfo', '-cert', self.cert_path], stdin="")
+        not_after = datetime.strptime(out['not_after'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc)
+        until = not_after - datetime.now(timezone.utc)
+        if until.days < 30:
+            return True
+        return False
+
     def ensure(self):
-        if self.key_exists and self.cert_exists:
+        if self.key_exists and self.cert_exists and not self.cert_expires_soon:
             return
 
-        logger.info("{}: Generating...".format(self))
-        key, csr = self.ca.gen_key(self.hosts, o=self.o, ou=self.ou, save=self.key)
+        key = None
+        if not self.key_exists:
+            logger.info("{}: Generating key...".format(self))
+            key, csr = self.ca.gen_key(self.hosts, o=self.o, ou=self.ou, save=self.key)
+        else:
+            logger.info("{}: Renewing certificate...".format(self))
+            # Use already existing key
+            csr = self.ca.gen_csr(self.key_path, self.hosts, o=self.o, ou=self.ou)
         self.ca.sign(csr, save=self.cert, profile=self.profile)
 
     def upload(self, c, remote_cert, remote_key, concat_ca=False):
diff --git a/cluster/clustercfg/clustercfg.py b/cluster/clustercfg/clustercfg.py
index eb9f52d..1396826 100644
--- a/cluster/clustercfg/clustercfg.py
+++ b/cluster/clustercfg/clustercfg.py
@@ -175,7 +175,7 @@
         ca_kube = ca.CA(ss, certs_root, 'kube', 'kubernetes main CA')
 
         # Make prodvider intermediate CA.
-        ca_kube.make_cert('ca-kube-prodvider', o='Warsaw Hackerspace', ou='kubernetes prodvider intermediate', hosts=['kubernetes prodvider intermediate CA'], profile='intermediate')
+        ca_kube.make_cert('ca-kube-prodvider', o='Warsaw Hackerspace', ou='kubernetes prodvider intermediate', hosts=['kubernetes prodvider intermediate CA'], profile='intermediate').ensure()
 
         # Make kubelet certificate (per node).
         ca_kube.make_cert('kube-kubelet-'+fqdn, o='system:nodes', ou='Kubelet', hosts=['system:node:'+fqdn, fqdn])
@@ -225,6 +225,9 @@
         return nodestrap(sys.argv[2:], nocerts=True)
     elif mode == "admincreds":
         return admincreds(sys.argv[2:])
+    elif mode == "smoketest":
+        sys.stdout.write("Smoke test passed.")
+        return 0
     else:
         usage()
         return 1
diff --git a/cluster/doc/admin.md b/cluster/doc/admin.md
new file mode 100644
index 0000000..1dfb50a
--- /dev/null
+++ b/cluster/doc/admin.md
@@ -0,0 +1,119 @@
+HSCloud Clusters
+================
+
+Admin documentation. For user documentation, see [//cluster/doc/user.md](/cluster/doc/user.md).
+
+Current cluster: `k0.hswaw.net`
+
+Persistent Storage (waw2)
+-------------------------
+
+HDDs on bc01n0{1-3}. 3TB total capacity. Don't use this as this pool should go
+away soon (the disks are slow, the network is slow and the RAID controllers
+lie). Use ceph-waw3 instead.
+
+The following storage classes use this cluster:
+
+ - `waw-hdd-paranoid-1` - 3 replicas
+ - `waw-hdd-redundant-1` - erasure coded 2.1
+ - `waw-hdd-yolo-1` - unreplicated (you _will_ lose your data)
+ - `waw-hdd-redundant-1-object` - erasure coded 2.1 object store
+
+Rados Gateway (S3) is available at https://object.ceph-waw2.hswaw.net/. To
+create a user, ask an admin.
+
+PersistentVolumes currently bound to PersistentVolumeClaims get automatically
+backed up (hourly for the next 48 hours, then once every 4 weeks, then once
+every month for a year).
+
+Persistent Storage (waw3)
+-------------------------
+
+HDDs on dcr01s2{2,4}. 40TB total capacity for now. Use this.
+
+The following storage classes use this cluster:
+
+ - `waw-hdd-yolo-3` - 1 replica
+ - `waw-hdd-redundant-3` - 2 replicas
+ - `waw-hdd-redundant-3-object` - 2 replicas, object store
+
+Rados Gateway (S3) is available at https://object.ceph-waw3.hswaw.net/. To
+create a user, ask an admin.
+
+PersistentVolumes currently bound to PVCs get automatically backed up (hourly
+for the next 48 hours, then once every 4 weeks, then once every month for a
+year).
+
+Administration
+==============
+
+Provisioning nodes
+------------------
+
+ - bring up a new node with nixos, the configuration doesn't matter and will be
+   nuked anyway
+ - edit cluster/nix/defs-machines.nix
+ - `bazel run //cluster/clustercfg nodestrap bc01nXX.hswaw.net`
+
+Applying kubecfg state
+----------------------
+
+First, decrypt/sync all secrets:
+
+    secretstore sync cluster/secrets/
+
+Then, run kubecfg. There's multiple top-level 'view' files that you can run,
+all located in `//cluster/kube`.  All of them use `k0.libsonnet` as the master
+state of Kubernetes configuration, just expose subsets of it to work around the
+fact that kubecfg gets somewhat slow with a lot of resources.
+
+ - `k0.jsonnet`: everything that is defined for k0 in `//cluster/kube/...`.
+ - `k0-core.jsonnet`: definitions that re in common across all clusters
+   (networking, registry, etc), without Rook.
+ - `k0-registry.jsonnet`: just the docker registry on k0 (useful when changing
+   ACLs).
+ - `k0-ceph.jsonnet`: everything ceph/rook related on k0.
+
+When in doubt, run `k0.jsonnet`. There's no harm in doing it, it might just be
+slow. Running individual files without realizing that whatever change you
+implemented also influenced something that was rendered in another file can
+cause to production inconsistencies.
+
+Feel free to add more view files for typical administrative tasks.
+
+Ceph - Debugging
+-----------------
+
+We run Ceph via Rook. The Rook operator is running in the `ceph-rook-system`
+namespace. To debug Ceph issues, start by looking at its logs.
+
+A dashboard is available at https://ceph-waw2.hswaw.net/ and
+https://ceph-waw3.hswaw.net, to get the admin password run:
+
+    kubectl -n ceph-waw2 get secret rook-ceph-dashboard-password -o yaml | grep "password:" | awk '{print $2}' | base64 --decode ; echo
+    kubectl -n ceph-waw3 get secret rook-ceph-dashboard-password -o yaml | grep "password:" | awk '{print $2}' | base64 --decode ; echo
+
+
+Ceph - Backups
+--------------
+
+Kubernetes PVs backed in Ceph RBDs get backed up using Benji. An hourly cronjob
+runs in every Ceph cluster. You can also manually trigger a run by doing:
+
+    kubectl -n ceph-waw2 create job --from=cronjob/ceph-waw2-benji ceph-waw2-benji-manual-$(date +%s)
+    kubectl -n ceph-waw3 create job --from=cronjob/ceph-waw3-benji ceph-waw3-benji-manual-$(date +%s)
+
+Ceph ObjectStorage pools (RADOSGW) are _not_ backed up yet!
+
+Ceph - Object Storage
+---------------------
+
+To create an object store user consult rook.io manual
+(https://rook.io/docs/rook/v0.9/ceph-object-store-user-crd.html).
+User authentication secret is generated in ceph cluster namespace
+(`ceph-waw{2,3}`), thus may need to be manually copied into application namespace.
+(see `app/registry/prod.jsonnet` comment)
+
+`tools/rook-s3cmd-config` can be used to generate test configuration file for
+s3cmd.  Remember to append `:default-placement` to your region name (ie.
+`waw-hdd-redundant-3-object:default-placement`)
diff --git a/cluster/doc/index.md b/cluster/doc/index.md
new file mode 100644
index 0000000..afd04e4
--- /dev/null
+++ b/cluster/doc/index.md
@@ -0,0 +1,6 @@
+Warsaw Hackerspace Kubernetes Cluster
+=====================================
+
+**User documentation**: [user.md](user.md).
+
+**Admin documentation**: [admin.md](admin.md).
diff --git a/cluster/doc/user.md b/cluster/doc/user.md
new file mode 100644
index 0000000..6dd6938
--- /dev/null
+++ b/cluster/doc/user.md
@@ -0,0 +1,58 @@
+Warsaw Hackerspace Kubernetes Clusters
+======================================
+
+End-user^Whacker documentation.
+
+Intro
+-----
+
+We run Kubernetes, a cluster system on our production machines. This allows you to schedule software to run without having to worry about traditional deployment, or where your particular piece of code is actually running. This document will not teach you how to use Kubernetes, but will give you a short hands-on example on how to access it, and then point you in the right direction for general documentation to follow.
+
+Accessing Kubernetes
+--------------------
+
+Kubernetes is accessed fully via an API, for which there exists a standard command line tool: `kubectl`. If you've check out hscloud and followed the instructions in [//README.md]("/README.md"), you should have that tool built and available for you to use.
+
+Before you can use `kubectl`, however, you will need to authenticate yourself. To do that, run `prodaccess`. This will issue you short-term (~hours) credentials that `kubectl` can then pass on to Kubernetes to authenticate itself.
+
+    $ prodaccess
+    Enter SSO/LDAP password for q3k@hackerspace.pl: 
+    Good evening professor. I see you have driven here in your Ferrari.
+
+If `prodaccess` is not on your $PATH, ensure you have sourced `env.sh` from the root of hscloud and ran `tools/install.sh`.
+
+By default, `prodaccess` will use your local user name to authenticate as `<user>@hackerspce.pl`. If your Hackerspace SSO name is different, specify it using the `-u` flag to prodaccess, eg. `prodaccess -u informatic`.
+
+You can now check that you indeed have access to Kubernetes:
+
+    $ kubectl version   # show version of Kubernetes
+    $ kubectl top nodes # show node (machine/server) statistics
+
+You are now fully set up to schedule your own jobs on `k0.hswaw.net`, our currently only Kubernetes cluster.
+
+Running Stuff
+-------------
+
+We have a fairly extensive role-based access control system set up to provide a level of multi-tenancy of our Kubernetes cluster. What this means is that you will not be able to modify other people's stuff. Indeed, by default, you barely have any access. So as you can experiment with Kubernetes, we automatically provision you a personal namespace (`personal-$USER`) in Kubernetes. This acts as your own playground, where you can run anything you want, as long as it doesn't eat into our resources too much.
+
+For example, to run an Alpine Linux Docker image in your own namespace:
+
+    kubectl -n personal-$USER run --image=alpine:latest -it foo
+
+This will create a Kubernetes deployment named foo, running the `alpine:latest` Docker image, and drop you in an interactive shell in it. Naturally, replace `$USER` with your SSO username if it's different from your system username.
+
+Once you're done, delete the Deployment:
+
+    kubectl -n personal-$USER delete deployment foo
+
+Pod Security
+------------
+
+Apart from the RBAC (role based access control) that prevents you from poling at things that you shouldn't over the API, we have one more security measure in place. Throught a Kubernetes mechanism called 'PodSecurityPolicy' we limit what pods (ie. containers) can do. Notably, pods will by default not be able to access any host data, run in privileged mode, or even setuid to a different uid. The most notable side effect of this is that some basic system tools within pods will not work: ie., apt on Ubuntu.
+
+More Kubernetes
+---------------
+
+We highly recommend following the [Kubernetes Basics](https://kubernetes.io/docs/tutorials/kubernetes-basics/) tutorial as a first step in using Kubernetes for real world applications.
+
+For defining production jobs, we use a language called `Jsonnet` via a tool called `kubecfg`. This is to replace some more popular tools that other Kubernetes systems use, eg. Helm. For more information about that, ping q3k so that he writes a codelab about it :).
diff --git a/cluster/kube/cluster.jsonnet b/cluster/kube/cluster.jsonnet
deleted file mode 100644
index 7525911..0000000
--- a/cluster/kube/cluster.jsonnet
+++ /dev/null
@@ -1,489 +0,0 @@
-# Top level cluster configuration.
-
-local kube = import "../../kube/kube.libsonnet";
-local policies = import "../../kube/policies.libsonnet";
-
-local calico = import "lib/calico.libsonnet";
-local certmanager = import "lib/cert-manager.libsonnet";
-local cockroachdb = import "lib/cockroachdb.libsonnet";
-local coredns = import "lib/coredns.libsonnet";
-local metallb = import "lib/metallb.libsonnet";
-local metrics = import "lib/metrics.libsonnet";
-local nginx = import "lib/nginx.libsonnet";
-local prodvider = import "lib/prodvider.libsonnet";
-local registry = import "lib/registry.libsonnet";
-local rook = import "lib/rook.libsonnet";
-local pki = import "lib/pki.libsonnet";
-
-local Cluster(short, realm) = {
-    local cluster = self,
-    local cfg = cluster.cfg,
-
-    short:: short,
-    realm:: realm,
-    fqdn:: "%s.%s" % [cluster.short, cluster.realm],
-
-    cfg:: {
-        // Storage class used for internal services (like registry). This must
-        // be set to a valid storage class. This can either be a cloud provider class
-        // (when running on GKE &co) or a storage class created using rook.
-        storageClassNameRedundant: error "storageClassNameRedundant must be set",
-    },
-
-    // These are required to let the API Server contact kubelets.
-    crAPIServerToKubelet: kube.ClusterRole("system:kube-apiserver-to-kubelet") {
-        metadata+: {
-            annotations+: {
-                "rbac.authorization.kubernetes.io/autoupdate": "true",
-            },
-            labels+: {
-                "kubernetes.io/bootstrapping": "rbac-defaults",
-            },
-        },
-        rules: [
-            {
-                apiGroups: [""],
-                resources: ["nodes/%s" % r for r in [ "proxy", "stats", "log", "spec", "metrics" ]],
-                verbs: ["*"],
-            },
-        ],
-    },
-    crbAPIServer: kube.ClusterRoleBinding("system:kube-apiserver") {
-        roleRef: {
-            apiGroup: "rbac.authorization.k8s.io",
-            kind: "ClusterRole",
-            name: cluster.crAPIServerToKubelet.metadata.name,
-        },
-        subjects: [
-            {
-                apiGroup: "rbac.authorization.k8s.io",
-                kind: "User",
-                # A cluster API Server authenticates with a certificate whose CN is == to the FQDN of the cluster.
-                name: cluster.fqdn,
-            },
-        ],
-    },
-
-    // This ClusteRole is bound to all humans that log in via prodaccess/prodvider/SSO.
-    // It should allow viewing of non-sensitive data for debugability and openness.
-    crViewer: kube.ClusterRole("system:viewer") {
-        rules: [
-            {
-                apiGroups: [""],
-                resources: [
-                    "nodes",
-                    "namespaces",
-                    "pods",
-                    "configmaps",
-                    "services",
-                ],
-                verbs: ["list"],
-            },
-            {
-                apiGroups: ["metrics.k8s.io"],
-                resources: [
-                    "nodes",
-                    "pods",
-                ],
-                verbs: ["list"],
-            },
-            {
-                apiGroups: ["apps"],
-                resources: [
-                    "statefulsets",
-                ],
-                verbs: ["list"],
-            },
-            {
-                apiGroups: ["extensions"],
-                resources: [
-                    "deployments",
-                    "ingresses",
-                ],
-                verbs: ["list"],
-            }
-        ],
-    },
-    // This ClusterRole is applied (scoped to personal namespace) to all humans.
-    crFullInNamespace: kube.ClusterRole("system:admin-namespace") {
-        rules: [
-            {
-                apiGroups: ["*"],
-                resources: ["*"],
-                verbs: ["*"],
-            },
-        ],
-    },
-    // This ClusterRoleBindings allows root access to cluster admins.
-    crbAdmins: kube.ClusterRoleBinding("system:admins") {
-        roleRef: {
-            apiGroup: "rbac.authorization.k8s.io",
-            kind: "ClusterRole",
-            name: "cluster-admin",
-        },
-        subjects: [
-            {
-                apiGroup: "rbac.authorization.k8s.io",
-                kind: "User",
-                name: user + "@hackerspace.pl",
-            } for user in [
-                "q3k",
-                "implr",
-                "informatic",
-            ]
-        ],
-    },
-
-    podSecurityPolicies: policies.Cluster {},
-
-    allowInsecureNamespaces: [
-        policies.AllowNamespaceInsecure("kube-system"),
-        policies.AllowNamespaceInsecure("metallb-system"),
-        # TODO(q3k): fix this?
-        policies.AllowNamespaceInsecure("ceph-waw2"),
-        policies.AllowNamespaceInsecure("ceph-waw3"),
-        policies.AllowNamespaceInsecure("matrix"),
-        policies.AllowNamespaceInsecure("registry"),
-        policies.AllowNamespaceInsecure("internet"),
-    ],
-
-    // Allow all service accounts (thus all controllers) to create secure pods.
-    crbAllowServiceAccountsSecure: kube.ClusterRoleBinding("policy:allow-all-secure") {
-        roleRef_: cluster.podSecurityPolicies.secureRole,
-        subjects: [
-            {
-                kind: "Group",
-                apiGroup: "rbac.authorization.k8s.io",
-                name: "system:serviceaccounts",
-            }
-        ],
-    },
-
-    // Calico network fabric
-    calico: calico.Environment {},
-    // CoreDNS for this cluster.
-    dns: coredns.Environment {
-        cfg+: {
-            cluster_domains: [
-                "cluster.local",
-                cluster.fqdn,
-            ],
-        },
-    },
-    // Metrics Server
-    metrics: metrics.Environment {},
-    // Metal Load Balancer
-    metallb: metallb.Environment {
-        cfg+: {
-            peers: [
-                {
-                    "peer-address": "185.236.240.33",
-                    "peer-asn": 65001,
-                    "my-asn": 65002,
-                },
-            ],
-            addressPools: [
-                {
-                    name: "public-v4-1",
-                    protocol: "bgp",
-                    addresses: [
-                        "185.236.240.48/28",
-                    ],
-                },
-                {
-                    name: "public-v4-2",
-                    protocol: "bgp",
-                    addresses: [
-                        "185.236.240.112/28"
-                    ],
-                },
-            ],
-        },
-    },
-    // Main nginx Ingress Controller
-    nginx: nginx.Environment {},
-    certmanager: certmanager.Environment {},
-    issuer: kube.ClusterIssuer("letsencrypt-prod") {
-        spec: {
-            acme: {
-                server: "https://acme-v02.api.letsencrypt.org/directory",
-                email: "bofh@hackerspace.pl",
-                privateKeySecretRef: {
-                    name: "letsencrypt-prod"
-                },
-                http01: {},
-            },
-        },
-    },
-
-    // Rook Ceph storage
-    rook: rook.Operator {
-        operator+: {
-            spec+: {
-                // TODO(q3k): Bring up the operator again when stability gets fixed
-                // See: https://github.com/rook/rook/issues/3059#issuecomment-492378873
-                replicas: 1,
-            },
-        },
-    },
-
-    // Docker registry
-    registry: registry.Environment {
-        cfg+: {
-            domain: "registry.%s" % [cluster.fqdn],
-            storageClassName: cfg.storageClassNameParanoid,
-            objectStorageName: "waw-hdd-redundant-2-object",
-        },
-    },
-
-    // TLS PKI machinery
-    pki: pki.Environment(cluster.short, cluster.realm),
-
-    // Prodvider
-    prodvider: prodvider.Environment {
-        cfg+: {
-            apiEndpoint: "kubernetes.default.svc.%s" % [cluster.fqdn],
-        },
-    },
-};
-
-
-{
-    k0: {
-        local k0 = self,
-        cluster: Cluster("k0", "hswaw.net") {
-            cfg+: {
-                storageClassNameParanoid: k0.ceph.waw2Pools.blockParanoid.name,
-            },
-        },
-        cockroach: {
-            waw2: cockroachdb.Cluster("crdb-waw1") {
-                cfg+: {
-                    topology: [
-                        { name: "bc01n01", node: "bc01n01.hswaw.net" },
-                        { name: "bc01n02", node: "bc01n02.hswaw.net" },
-                        { name: "bc01n03", node: "bc01n03.hswaw.net" },
-                    ],
-                    hostPath: "/var/db/crdb-waw1",
-                },
-            },
-            clients: {
-                cccampix: k0.cockroach.waw2.Client("cccampix"),
-                cccampixDev: k0.cockroach.waw2.Client("cccampix-dev"),
-                buglessDev: k0.cockroach.waw2.Client("bugless-dev"),
-            },
-        },
-        ceph: {
-            // waw1 cluster - dead as of 2019/08/06, data corruption
-            // waw2 cluster
-            waw2: rook.Cluster(k0.cluster.rook, "ceph-waw2") {
-                spec: {
-                    mon: {
-                        count: 3,
-                        allowMultiplePerNode: false,
-                    },
-                    storage: {
-                        useAllNodes: false,
-                        useAllDevices: false,
-                        config: {
-                            databaseSizeMB: "1024",
-                            journalSizeMB: "1024",
-                        },
-                        nodes: [
-                            {
-                                name: "bc01n01.hswaw.net",
-                                location: "rack=dcr01 chassis=bc01 host=bc01n01",
-                                devices: [ { name: "sda" } ],
-                            },
-                            {
-                                name: "bc01n02.hswaw.net",
-                                location: "rack=dcr01 chassis=bc01 host=bc01n02",
-                                devices: [ { name: "sda" } ],
-                            },
-                            {
-                                name: "bc01n03.hswaw.net",
-                                location: "rack=dcr01 chassis=bc01 host=bc01n03",
-                                devices: [ { name: "sda" } ],
-                            },
-                        ],
-                    },
-                    benji:: {
-                        metadataStorageClass: "waw-hdd-paranoid-2",
-                        encryptionPassword: std.split((importstr "../secrets/plain/k0-benji-encryption-password"), '\n')[0],
-                        pools: [
-                            "waw-hdd-redundant-2",
-                            "waw-hdd-redundant-2-metadata",
-                            "waw-hdd-paranoid-2",
-                            "waw-hdd-yolo-2",
-                        ],
-                        s3Configuration: {
-                            awsAccessKeyId: "RPYZIROFXNLQVU2WJ4R3",
-                            awsSecretAccessKey: std.split((importstr "../secrets/plain/k0-benji-secret-access-key"), '\n')[0],
-                            bucketName: "benji-k0-backups",
-                            endpointUrl: "https://s3.eu-central-1.wasabisys.com/",
-                        },
-                    }
-                },
-            },
-            waw2Pools: {
-                // redundant block storage
-                blockRedundant: rook.ECBlockPool(k0.ceph.waw2, "waw-hdd-redundant-2") {
-                    spec: {
-                        failureDomain: "host",
-                        erasureCoded: {
-                            dataChunks: 2,
-                            codingChunks: 1,
-                        },
-                    },
-                },
-                // paranoid block storage (3 replicas)
-                blockParanoid: rook.ReplicatedBlockPool(k0.ceph.waw2, "waw-hdd-paranoid-2") {
-                    spec: {
-                        failureDomain: "host",
-                        replicated: {
-                            size: 3,
-                        },
-                    },
-                },
-                // yolo block storage (no replicas!)
-                blockYolo: rook.ReplicatedBlockPool(k0.ceph.waw2, "waw-hdd-yolo-2") {
-                    spec: {
-                        failureDomain: "host",
-                        replicated: {
-                            size: 1,
-                        },
-                    },
-                },
-                objectRedundant: rook.S3ObjectStore(k0.ceph.waw2, "waw-hdd-redundant-2-object") {
-                    spec: {
-                        metadataPool: {
-                            failureDomain: "host",
-                            replicated: { size: 3 },
-                        },
-                        dataPool: {
-                            failureDomain: "host",
-                            erasureCoded: {
-                                dataChunks: 2,
-                                codingChunks: 1,
-                            },
-                        },
-                    },
-                },
-            },
-            waw3: rook.Cluster(k0.cluster.rook, "ceph-waw3") {
-                spec: {
-                    mon: {
-                        count: 1,
-                        allowMultiplePerNode: false,
-                    },
-                    storage: {
-                        useAllNodes: false,
-                        useAllDevices: false,
-                        config: {
-                            databaseSizeMB: "1024",
-                            journalSizeMB: "1024",
-                        },
-                        nodes: [
-                            {
-                                name: "dcr01s22.hswaw.net",
-                                location: "rack=dcr01 host=dcr01s22",
-                                devices: [
-                                    // https://github.com/rook/rook/issues/1228
-                                    //{ name: "disk/by-id/wwan-0x" + wwan }
-                                    //for wwan in [
-                                    //    "5000c5008508c433",
-                                    //    "5000c500850989cf",
-                                    //    "5000c5008508f843",
-                                    //    "5000c5008508baf7",
-                                    //]
-                                    { name: "sdn" },
-                                    { name: "sda" },
-                                    { name: "sdb" },
-                                    { name: "sdc" },
-                                ],
-                            },
-                            {
-                                name: "dcr01s24.hswaw.net",
-                                location: "rack=dcr01 host=dcr01s22",
-                                devices: [
-                                    // https://github.com/rook/rook/issues/1228
-                                    //{ name: "disk/by-id/wwan-0x" + wwan }
-                                    //for wwan in [
-                                    //    "5000c5008508ee03",
-                                    //    "5000c5008508c9ef",
-                                    //    "5000c5008508df33",
-                                    //    "5000c5008508dd3b",
-                                    //]
-                                    { name: "sdm" },
-                                    { name: "sda" },
-                                    { name: "sdb" },
-                                    { name: "sdc" },
-                                ],
-                            },
-                        ],
-                    },
-                    benji:: {
-                        metadataStorageClass: "waw-hdd-redundant-3",
-                        encryptionPassword: std.split((importstr "../secrets/plain/k0-benji-encryption-password"), '\n')[0],
-                        pools: [
-                            "waw-hdd-redundant-3",
-                            "waw-hdd-redundant-3-metadata",
-                            "waw-hdd-yolo-3",
-                        ],
-                        s3Configuration: {
-                            awsAccessKeyId: "RPYZIROFXNLQVU2WJ4R3",
-                            awsSecretAccessKey: std.split((importstr "../secrets/plain/k0-benji-secret-access-key"), '\n')[0],
-                            bucketName: "benji-k0-backups-waw3",
-                            endpointUrl: "https://s3.eu-central-1.wasabisys.com/",
-                        },
-                    }
-                },
-            },
-            waw3Pools: {
-                // redundant block storage
-                blockRedundant: rook.ECBlockPool(k0.ceph.waw3, "waw-hdd-redundant-3") {
-                    metadataReplicas: 2,
-                    spec: {
-                        failureDomain: "host",
-                        replicated: {
-                          size: 2,
-                        },
-                    },
-                },
-                // yolo block storage (low usage, no host redundancy)
-                blockYolo: rook.ReplicatedBlockPool(k0.ceph.waw3, "waw-hdd-yolo-3") {
-                    spec: {
-                        failureDomain: "osd",
-                        erasureCoded: {
-                            dataChunks: 12,
-                            codingChunks: 4,
-                        },
-                    },
-                },
-                objectRedundant: rook.S3ObjectStore(k0.ceph.waw3, "waw-hdd-redundant-3-object") {
-                    spec: {
-                        metadataPool: {
-                            failureDomain: "host",
-                            replicated: { size: 2 },
-                        },
-                        dataPool: {
-                            failureDomain: "host",
-                            replicated: { size: 2 },
-                        },
-                    },
-                },
-            },
-        },
-
-        # Used for owncloud.hackerspace.pl, which for now lices on boston-packets.hackerspace.pl.
-        nextcloudWaw3: kube.CephObjectStoreUser("nextcloud") {
-            metadata+: {
-                namespace: "ceph-waw3",
-            },
-            spec: {
-                store: "waw-hdd-redundant-3-object",
-                displayName: "nextcloud",
-            },
-        },
-    },
-}
diff --git a/cluster/kube/cluster.libsonnet b/cluster/kube/cluster.libsonnet
new file mode 100644
index 0000000..c42ee8a
--- /dev/null
+++ b/cluster/kube/cluster.libsonnet
@@ -0,0 +1,221 @@
+# Common cluster configuration.
+# This defines what Kubernetes resources are required to turn a bare k8s
+# deployment into a fully working cluster.
+# These assume that you're running on bare metal, and using the corresponding
+# NixOS deployment that we do.
+
+local kube = import "../../kube/kube.libsonnet";
+local policies = import "../../kube/policies.libsonnet";
+
+local calico = import "lib/calico.libsonnet";
+local certmanager = import "lib/cert-manager.libsonnet";
+local coredns = import "lib/coredns.libsonnet";
+local metallb = import "lib/metallb.libsonnet";
+local metrics = import "lib/metrics.libsonnet";
+local nginx = import "lib/nginx.libsonnet";
+local prodvider = import "lib/prodvider.libsonnet";
+local rook = import "lib/rook.libsonnet";
+local pki = import "lib/pki.libsonnet";
+
+{
+    Cluster(short, realm):: {
+        local cluster = self,
+        local cfg = cluster.cfg,
+
+        short:: short,
+        realm:: realm,
+        fqdn:: "%s.%s" % [cluster.short, cluster.realm],
+
+        cfg:: {
+            // Storage class used for internal services (like registry). This must
+            // be set to a valid storage class. This can either be a cloud provider class
+            // (when running on GKE &co) or a storage class created using rook.
+            storageClassNameRedundant: error "storageClassNameRedundant must be set",
+        },
+
+        // These are required to let the API Server contact kubelets.
+        crAPIServerToKubelet: kube.ClusterRole("system:kube-apiserver-to-kubelet") {
+            metadata+: {
+                annotations+: {
+                    "rbac.authorization.kubernetes.io/autoupdate": "true",
+                },
+                labels+: {
+                    "kubernetes.io/bootstrapping": "rbac-defaults",
+                },
+            },
+            rules: [
+                {
+                    apiGroups: [""],
+                    resources: ["nodes/%s" % r for r in [ "proxy", "stats", "log", "spec", "metrics" ]],
+                    verbs: ["*"],
+                },
+            ],
+        },
+        crbAPIServer: kube.ClusterRoleBinding("system:kube-apiserver") {
+            roleRef: {
+                apiGroup: "rbac.authorization.k8s.io",
+                kind: "ClusterRole",
+                name: cluster.crAPIServerToKubelet.metadata.name,
+            },
+            subjects: [
+                {
+                    apiGroup: "rbac.authorization.k8s.io",
+                    kind: "User",
+                    # A cluster API Server authenticates with a certificate whose CN is == to the FQDN of the cluster.
+                    name: cluster.fqdn,
+                },
+            ],
+        },
+
+        // This ClusterRole is bound to all humans that log in via prodaccess/prodvider/SSO.
+        // It should allow viewing of non-sensitive data for debugability and openness.
+        crViewer: kube.ClusterRole("system:viewer") {
+            rules: [
+                {
+                    apiGroups: [""],
+                    resources: [
+                        "nodes",
+                        "namespaces",
+                        "pods",
+                        "configmaps",
+                        "services",
+                    ],
+                    verbs: ["list"],
+                },
+                {
+                    apiGroups: ["metrics.k8s.io"],
+                    resources: [
+                        "nodes",
+                        "pods",
+                    ],
+                    verbs: ["list"],
+                },
+                {
+                    apiGroups: ["apps"],
+                    resources: [
+                        "statefulsets",
+                    ],
+                    verbs: ["list"],
+                },
+                {
+                    apiGroups: ["extensions"],
+                    resources: [
+                        "deployments",
+                        "ingresses",
+                    ],
+                    verbs: ["list"],
+                }
+            ],
+        },
+        // This ClusterRole is applied (scoped to personal namespace) to all humans.
+        crFullInNamespace: kube.ClusterRole("system:admin-namespace") {
+            rules: [
+                {
+                    apiGroups: ["", "extensions", "apps"],
+                    resources: ["*"],
+                    verbs: ["*"],
+                },
+                {
+                    apiGroups: ["batch"],
+                    resources: ["jobs", "cronjobs"],
+                    verbs: ["*"],
+                },
+            ],
+        },
+        // This ClusterRoleBindings allows root access to cluster admins.
+        crbAdmins: kube.ClusterRoleBinding("system:admins") {
+            roleRef: {
+                apiGroup: "rbac.authorization.k8s.io",
+                kind: "ClusterRole",
+                name: "cluster-admin",
+            },
+            subjects: [
+                {
+                    apiGroup: "rbac.authorization.k8s.io",
+                    kind: "User",
+                    name: user + "@hackerspace.pl",
+                } for user in [
+                    "q3k",
+                    "implr",
+                    "informatic",
+                ]
+            ],
+        },
+
+        podSecurityPolicies: policies.Cluster {},
+
+        allowInsecureNamespaces: [
+            policies.AllowNamespaceInsecure("kube-system"),
+            policies.AllowNamespaceInsecure("metallb-system"),
+        ],
+
+        // Allow all service accounts (thus all controllers) to create secure pods.
+        crbAllowServiceAccountsSecure: kube.ClusterRoleBinding("policy:allow-all-secure") {
+            roleRef_: cluster.podSecurityPolicies.secureRole,
+            subjects: [
+                {
+                    kind: "Group",
+                    apiGroup: "rbac.authorization.k8s.io",
+                    name: "system:serviceaccounts",
+                }
+            ],
+        },
+
+        // Calico network fabric
+        calico: calico.Environment {},
+
+        // CoreDNS for this cluster.
+        dns: coredns.Environment {
+            cfg+: {
+                cluster_domains: [
+                    "cluster.local",
+                    cluster.fqdn,
+                ],
+            },
+        },
+
+        // Metrics Server
+        metrics: metrics.Environment {},
+
+        // Metal Load Balancer
+        metallb: metallb.Environment {},
+
+        // Main nginx Ingress Controller
+        nginx: nginx.Environment {},
+
+        // Cert-manager (Let's Encrypt, CA, ...)
+        certmanager: certmanager.Environment {},
+
+        issuer: kube.ClusterIssuer("letsencrypt-prod") {
+            spec: {
+                acme: {
+                    server: "https://acme-v02.api.letsencrypt.org/directory",
+                    email: "bofh@hackerspace.pl",
+                    privateKeySecretRef: {
+                        name: "letsencrypt-prod"
+                    },
+                    http01: {},
+                },
+            },
+        },
+
+        // Rook Ceph storage operator.
+        rook: rook.Operator {
+            operator+: {
+                spec+: {
+                    replicas: 1,
+                },
+            },
+        },
+
+        // TLS PKI machinery (compatibility with mirko)
+        pki: pki.Environment(cluster.short, cluster.realm),
+
+        // Prodvider
+        prodvider: prodvider.Environment {
+            cfg+: {
+                apiEndpoint: "kubernetes.default.svc.%s" % [cluster.fqdn],
+            },
+        },
+    },
+}
diff --git a/cluster/kube/k0-ceph.jsonnet b/cluster/kube/k0-ceph.jsonnet
new file mode 100644
index 0000000..bc025d4
--- /dev/null
+++ b/cluster/kube/k0-ceph.jsonnet
@@ -0,0 +1,8 @@
+// Ceph operator (rook), pools, users.
+
+local k0 = (import "k0.libsonnet").k0;
+
+{
+    rook: k0.cluster.rook,
+    ceph: k0.ceph,
+}
diff --git a/cluster/kube/k0-core.jsonnet b/cluster/kube/k0-core.jsonnet
new file mode 100644
index 0000000..06c282e
--- /dev/null
+++ b/cluster/kube/k0-core.jsonnet
@@ -0,0 +1,6 @@
+// Only the 'core' cluster resources - ie., resource non specific to k0 in particular.
+// Without Rook, to speed things up.
+
+(import "k0.libsonnet").k0.cluster {
+    rook+:: {},
+}
diff --git a/cluster/kube/k0-registry.jsonnet b/cluster/kube/k0-registry.jsonnet
new file mode 100644
index 0000000..a2a6061
--- /dev/null
+++ b/cluster/kube/k0-registry.jsonnet
@@ -0,0 +1,3 @@
+// Only the registry running in k0.
+
+(import "k0.libsonnet").k0.registry
diff --git a/cluster/kube/k0.calico.yaml b/cluster/kube/k0.calico.yaml
new file mode 100644
index 0000000..eef2661
--- /dev/null
+++ b/cluster/kube/k0.calico.yaml
@@ -0,0 +1,78 @@
+# This is the current Calico configuration in k0.hswaw.net.
+# Unfortunately, we do not have Calico configured to use CRDs, and instead to
+# keep its resources separately from Kubernetes. Thus, this configuration
+# cannot be managed by Kubernetes/jsonnet. Instead, it must be applied manially:
+#
+#     calicoctl apply -f k0.calico.yaml
+
+apiVersion: projectcalico.org/v3
+kind: BGPConfiguration
+metadata:
+  name: default
+spec:
+  logSeverityScreen: Info
+  nodeToNodeMeshEnabled: true
+  asNumber: 65003
+
+---
+
+# metallb peer, must be compatible with the metallbc definition in k0.libsonnet.
+apiVersion: projectcalico.org/v3
+kind: BGPPeer
+metadata:
+  name: metallb
+spec:
+  peerIP: 127.0.0.1
+  asNumber: 65002
+
+---
+
+# ToR switch peering, must be compatible with the configuration on dcsw01.hswaw.net.
+apiVersion: projectcalico.org/v3
+kind: BGPPeer
+metadata:
+  name: dcsw01
+spec:
+  peerIP: 185.236.240.33
+  asNumber: 65001
+
+---
+
+# IP pool that's used by metallb. We mark it as disabled so that Calico doesn't
+# allocate Service IPs from it, just allow metallb routes from that pool to
+# pass through eBGP (otherwise Calico BIRD filter will filter them out).
+# Keep in sync with k0.libsonnet.
+apiVersion: projectcalico.org/v3
+kind: IPPool
+metadata:
+  name: public-v4-1
+spec:
+  cidr: 185.236.240.48/28
+  disabled: true
+---
+
+# IP pool that's used by metallb. We mark it as disabled so that Calico doesn't
+# allocate Service IPs from it, just allow metallb routes from that pool to
+# pass through eBGP (otherwise Calico BIRD filter will filter them out).
+# Keep in sync with k0.libsonnet.
+apiVersion: projectcalico.org/v3
+kind: IPPool
+metadata:
+  name: public-v4-2
+spec:
+  cidr: 185.236.240.112/28
+  disabled: true
+
+---
+
+# IP pool for the service network.
+apiVersion: projectcalico.org/v3
+kind: IPPool
+metadata:
+  name: default-ipv4-ippool
+spec:
+  blockSize: 26
+  cidr: 10.10.24.0/21
+  ipipMode: CrossSubnet
+  natOutgoing: true
+
diff --git a/cluster/kube/k0.jsonnet b/cluster/kube/k0.jsonnet
new file mode 100644
index 0000000..9658830
--- /dev/null
+++ b/cluster/kube/k0.jsonnet
@@ -0,0 +1,3 @@
+// Everything in the k0 cluster definition.
+
+(import "k0.libsonnet").k0
diff --git a/cluster/kube/k0.libsonnet b/cluster/kube/k0.libsonnet
new file mode 100644
index 0000000..45ae4c1
--- /dev/null
+++ b/cluster/kube/k0.libsonnet
@@ -0,0 +1,375 @@
+// k0.hswaw.net kubernetes cluster
+// This defines the cluster as a single object.
+// Use the sibling k0*.jsonnet 'view' files to actually apply the configuration.
+
+local kube = import "../../kube/kube.libsonnet";
+local policies = import "../../kube/policies.libsonnet";
+
+local cluster = import "cluster.libsonnet";
+
+local cockroachdb = import "lib/cockroachdb.libsonnet";
+local registry = import "lib/registry.libsonnet";
+local rook = import "lib/rook.libsonnet";
+
+{
+    k0: {
+        local k0 = self,
+        cluster: cluster.Cluster("k0", "hswaw.net") {
+            cfg+: {
+                storageClassNameParanoid: k0.ceph.waw3Pools.blockRedundant.name,
+            },
+            metallb+: {
+                cfg+: {
+                    // Peer with calico running on same node.
+                    peers: [
+                        {
+                            "peer-address": "127.0.0.1",
+                            "peer-asn": 65003,
+                            "my-asn": 65002,
+                        },
+                    ],
+                    // Public IP address pools. Keep in sync with k0.calico.yaml.
+                    addressPools: [
+                        {
+                            name: "public-v4-1",
+                            protocol: "bgp",
+                            addresses: [
+                                "185.236.240.48/28",
+                            ],
+                        },
+                        {
+                            name: "public-v4-2",
+                            protocol: "bgp",
+                            addresses: [
+                                "185.236.240.112/28"
+                            ],
+                        },
+                    ],
+                },
+            },
+        },
+
+        // Docker registry
+        registry: registry.Environment {
+            cfg+: {
+                domain: "registry.%s" % [k0.cluster.fqdn],
+                storageClassName: k0.cluster.cfg.storageClassNameParanoid,
+                objectStorageName: "waw-hdd-redundant-3-object",
+            },
+        },
+
+        // CockroachDB, running on bc01n{01,02,03}.
+        cockroach: {
+            waw2: cockroachdb.Cluster("crdb-waw1") {
+                cfg+: {
+                    topology: [
+                        { name: "bc01n01", node: "bc01n01.hswaw.net" },
+                        { name: "bc01n02", node: "bc01n02.hswaw.net" },
+                        { name: "bc01n03", node: "bc01n03.hswaw.net" },
+                    ],
+                    // Host path on SSD.
+                    hostPath: "/var/db/crdb-waw1",
+                    extraDNS: [
+                        "crdb-waw1.hswaw.net",
+                    ],
+                },
+            },
+            clients: {
+                cccampix: k0.cockroach.waw2.Client("cccampix"),
+                cccampixDev: k0.cockroach.waw2.Client("cccampix-dev"),
+                buglessDev: k0.cockroach.waw2.Client("bugless-dev"),
+                sso: k0.cockroach.waw2.Client("sso"),
+                herpDev: k0.cockroach.waw2.Client("herp-dev"),
+            },
+        },
+
+        ceph: {
+            // waw1 cluster - dead as of 2019/08/06, data corruption
+            // waw2 cluster: shitty 7200RPM 2.5" HDDs
+            waw2: rook.Cluster(k0.cluster.rook, "ceph-waw2") {
+                spec: {
+                    mon: {
+                        count: 3,
+                        allowMultiplePerNode: false,
+                    },
+                    storage: {
+                        useAllNodes: false,
+                        useAllDevices: false,
+                        config: {
+                            databaseSizeMB: "1024",
+                            journalSizeMB: "1024",
+                        },
+                        nodes: [
+                            {
+                                name: "bc01n01.hswaw.net",
+                                location: "rack=dcr01 chassis=bc01 host=bc01n01",
+                                devices: [ { name: "sda" } ],
+                            },
+                            {
+                                name: "bc01n02.hswaw.net",
+                                location: "rack=dcr01 chassis=bc01 host=bc01n02",
+                                devices: [ { name: "sda" } ],
+                            },
+                            {
+                                name: "bc01n03.hswaw.net",
+                                location: "rack=dcr01 chassis=bc01 host=bc01n03",
+                                devices: [ { name: "sda" } ],
+                            },
+                        ],
+                    },
+                    benji:: {
+                        metadataStorageClass: "waw-hdd-paranoid-2",
+                        encryptionPassword: std.split((importstr "../secrets/plain/k0-benji-encryption-password"), '\n')[0],
+                        pools: [
+                            "waw-hdd-redundant-2",
+                            "waw-hdd-redundant-2-metadata",
+                            "waw-hdd-paranoid-2",
+                            "waw-hdd-yolo-2",
+                        ],
+                        s3Configuration: {
+                            awsAccessKeyId: "RPYZIROFXNLQVU2WJ4R3",
+                            awsSecretAccessKey: std.split((importstr "../secrets/plain/k0-benji-secret-access-key"), '\n')[0],
+                            bucketName: "benji-k0-backups",
+                            endpointUrl: "https://s3.eu-central-1.wasabisys.com/",
+                        },
+                    }
+                },
+            },
+            waw2Pools: {
+                // redundant block storage
+                blockRedundant: rook.ECBlockPool(k0.ceph.waw2, "waw-hdd-redundant-2") {
+                    spec: {
+                        failureDomain: "host",
+                        erasureCoded: {
+                            dataChunks: 2,
+                            codingChunks: 1,
+                        },
+                    },
+                },
+                // paranoid block storage (3 replicas)
+                blockParanoid: rook.ReplicatedBlockPool(k0.ceph.waw2, "waw-hdd-paranoid-2") {
+                    spec: {
+                        failureDomain: "host",
+                        replicated: {
+                            size: 3,
+                        },
+                    },
+                },
+                // yolo block storage (no replicas!)
+                blockYolo: rook.ReplicatedBlockPool(k0.ceph.waw2, "waw-hdd-yolo-2") {
+                    spec: {
+                        failureDomain: "host",
+                        replicated: {
+                            size: 1,
+                        },
+                    },
+                },
+                objectRedundant: rook.S3ObjectStore(k0.ceph.waw2, "waw-hdd-redundant-2-object") {
+                    spec: {
+                        metadataPool: {
+                            failureDomain: "host",
+                            replicated: { size: 3 },
+                        },
+                        dataPool: {
+                            failureDomain: "host",
+                            erasureCoded: {
+                                dataChunks: 2,
+                                codingChunks: 1,
+                            },
+                        },
+                    },
+                },
+            },
+
+            // waw3: 6TB SAS 3.5" HDDs
+            waw3: rook.Cluster(k0.cluster.rook, "ceph-waw3") {
+                spec: {
+                    mon: {
+                        count: 3,
+                        allowMultiplePerNode: false,
+                    },
+                    storage: {
+                        useAllNodes: false,
+                        useAllDevices: false,
+                        config: {
+                            databaseSizeMB: "1024",
+                            journalSizeMB: "1024",
+                        },
+                        nodes: [
+                            {
+                                name: "dcr01s22.hswaw.net",
+                                location: "rack=dcr01 host=dcr01s22",
+                                devices: [
+                                    // https://github.com/rook/rook/issues/1228
+                                    //{ name: "disk/by-id/wwan-0x" + wwan }
+                                    //for wwan in [
+                                    //    "5000c5008508c433",
+                                    //    "5000c500850989cf",
+                                    //    "5000c5008508f843",
+                                    //    "5000c5008508baf7",
+                                    //]
+                                    { name: "sdn" },
+                                    { name: "sda" },
+                                    { name: "sdb" },
+                                    { name: "sdc" },
+                                ],
+                            },
+                            {
+                                name: "dcr01s24.hswaw.net",
+                                location: "rack=dcr01 host=dcr01s22",
+                                devices: [
+                                    // https://github.com/rook/rook/issues/1228
+                                    //{ name: "disk/by-id/wwan-0x" + wwan }
+                                    //for wwan in [
+                                    //    "5000c5008508ee03",
+                                    //    "5000c5008508c9ef",
+                                    //    "5000c5008508df33",
+                                    //    "5000c5008508dd3b",
+                                    //]
+                                    { name: "sdm" },
+                                    { name: "sda" },
+                                    { name: "sdb" },
+                                    { name: "sdc" },
+                                ],
+                            },
+                        ],
+                    },
+                    benji:: {
+                        metadataStorageClass: "waw-hdd-redundant-3",
+                        encryptionPassword: std.split((importstr "../secrets/plain/k0-benji-encryption-password"), '\n')[0],
+                        pools: [
+                            "waw-hdd-redundant-3",
+                            "waw-hdd-redundant-3-metadata",
+                            "waw-hdd-yolo-3",
+                        ],
+                        s3Configuration: {
+                            awsAccessKeyId: "RPYZIROFXNLQVU2WJ4R3",
+                            awsSecretAccessKey: std.split((importstr "../secrets/plain/k0-benji-secret-access-key"), '\n')[0],
+                            bucketName: "benji-k0-backups-waw3",
+                            endpointUrl: "https://s3.eu-central-1.wasabisys.com/",
+                        },
+                    }
+                },
+            },
+            waw3Pools: {
+                // redundant block storage
+                blockRedundant: rook.ECBlockPool(k0.ceph.waw3, "waw-hdd-redundant-3") {
+                    metadataReplicas: 2,
+                    spec: {
+                        failureDomain: "host",
+                        replicated: {
+                          size: 2,
+                        },
+                    },
+                },
+                // yolo block storage (low usage, no host redundancy)
+                blockYolo: rook.ReplicatedBlockPool(k0.ceph.waw3, "waw-hdd-yolo-3") {
+                    spec: {
+                        failureDomain: "osd",
+                        erasureCoded: {
+                            dataChunks: 12,
+                            codingChunks: 4,
+                        },
+                    },
+                },
+                // q3k's personal pool, used externally from k8s.
+                q3kRedundant: rook.ECBlockPool(k0.ceph.waw3, "waw-hdd-redundant-q3k-3") {
+                    metadataReplicas: 2,
+                    spec: {
+                        failureDomain: "host",
+                        replicated: {
+                          size: 2,
+                        },
+                    },
+                },
+                objectRedundant: rook.S3ObjectStore(k0.ceph.waw3, "waw-hdd-redundant-3-object") {
+                    spec: {
+                        metadataPool: {
+                            failureDomain: "host",
+                            replicated: { size: 2 },
+                        },
+                        dataPool: {
+                            failureDomain: "host",
+                            replicated: { size: 2 },
+                        },
+                    },
+                },
+            },
+
+            // Clients for S3/radosgw storage.
+            clients: {
+                # Used for owncloud.hackerspace.pl, which for now lives on boston-packets.hackerspace.pl.
+                nextcloudWaw3: kube.CephObjectStoreUser("nextcloud") {
+                    metadata+: {
+                        namespace: "ceph-waw3",
+                    },
+                    spec: {
+                        store: "waw-hdd-redundant-3-object",
+                        displayName: "nextcloud",
+                    },
+                },
+
+                # nuke@hackerspace.pl's personal storage.
+                nukePersonalWaw3: kube.CephObjectStoreUser("nuke-personal") {
+                    metadata+: {
+                        namespace: "ceph-waw3",
+                    },
+                    spec: {
+                        store: "waw-hdd-redundant-3-object",
+                        displayName: "nuke-personal",
+                    },
+                },
+
+                # patryk@hackerspace.pl's ArmA3 mod bucket.
+                cz2ArmaModsWaw3: kube.CephObjectStoreUser("cz2-arma3mods") {
+                    metadata+: {
+                        namespace: "ceph-waw3",
+                    },
+                    spec: {
+                        store: "waw-hdd-redundant-3-object",
+                        displayName: "cz2-arma3mods",
+                    },
+                },
+                # Buckets for spark pipelines
+                # TODO(implr): consider a second yolo-backed one for temp data
+                implrSparkWaw3: kube.CephObjectStoreUser("implr-spark") {
+                    metadata+: {
+                        namespace: "ceph-waw3",
+                    },
+                    spec: {
+                        store: "waw-hdd-redundant-3-object",
+                        displayName: "implr-spark",
+                    },
+                },
+                # q3k's personal user
+                q3kWaw3: kube.CephObjectStoreUser("q3k") {
+                    metadata+: {
+                        namespace: "ceph-waw3",
+                    },
+                    spec: {
+                        store: "waw-hdd-redundant-3-object",
+                        displayName: "q3k",
+                    },
+                },
+            },
+        },
+
+
+        # These are policies allowing for Insecure pods in some namespaces.
+        # A lot of them are spurious and come from the fact that we deployed
+        # these namespaces before we deployed the draconian PodSecurityPolicy
+        # we have now. This should be fixed by setting up some more granular
+        # policies, or fixing the workloads to not need some of the permission
+        # bits they use, whatever those might be.
+        # TODO(q3k): fix this?
+        unnecessarilyInsecureNamespaces: [
+            policies.AllowNamespaceInsecure("ceph-waw2"),
+            policies.AllowNamespaceInsecure("ceph-waw3"),
+            policies.AllowNamespaceInsecure("matrix"),
+            policies.AllowNamespaceInsecure("registry"),
+            policies.AllowNamespaceInsecure("internet"),
+            # TODO(implr): restricted policy with CAP_NET_ADMIN and tuntap, but no full root
+            policies.AllowNamespaceInsecure("implr-vpn"),
+        ],
+    },
+}
diff --git a/cluster/kube/lib/calico-bird-ipam.cfg.template b/cluster/kube/lib/calico-bird-ipam.cfg.template
new file mode 100644
index 0000000..8f96951
--- /dev/null
+++ b/cluster/kube/lib/calico-bird-ipam.cfg.template
@@ -0,0 +1,75 @@
+# This is forked from bird.cfg.template from calico running on k0.hswaw.net on 2020/09/21.
+# Changed vs. upstream (C-f HSCLOUD):
+#  - do not pass over RTD_UNREACHABLE routes obtained from mesh peers, to
+#    prevent them from being then passed over to ToRs. This prevents route leaks
+#    of metallb routes into ToRs from nodes that do not actually run that
+#    particular metallb service.
+#  - do not program RTD_UNREACHABLE routes into the kernel (these come from metallb, and
+#    programming them seems to break things)
+# Generated by confd
+
+filter calico_export_to_bgp_peers {
+  calico_aggr();
+{{- $static_key := "/staticroutes"}}
+{{- if ls $static_key}}
+
+  if ( proto ~ "Mesh_*" ) && ( dest = RTD_UNREACHABLE ) then { # HSCLOUD
+    reject;
+  }
+
+  # Export static routes.
+  {{- range ls $static_key}}
+    {{- $parts := split . "-"}}
+    {{- $cidr := join $parts "/"}}
+  if ( net ~ {{$cidr}} ) then { accept; }
+  {{- end}}
+{{- end}}
+{{range ls "/v1/ipam/v4/pool"}}{{$data := json (getv (printf "/v1/ipam/v4/pool/%s" .))}}
+  if ( net ~ {{$data.cidr}} ) then {
+    accept;
+  }
+{{- end}}
+  reject;
+}
+
+{{$network_key := printf "/bgp/v1/host/%s/network_v4" (getenv "NODENAME")}}
+filter calico_kernel_programming {
+{{- $reject_key := "/rejectcidrs"}}
+{{- if ls $reject_key}}
+
+  if ( dest = RTD_UNREACHABLE ) then { # HSCLOUD
+    reject;
+  }
+
+  # Don't program static routes into kernel.
+  {{- range ls $reject_key}}
+    {{- $parts := split . "-"}}
+    {{- $cidr := join $parts "/"}}
+  if ( net ~ {{$cidr}} ) then { reject; }
+  {{- end}}
+
+{{- end}}
+{{- if exists $network_key}}{{$network := getv $network_key}}
+{{range ls "/v1/ipam/v4/pool"}}{{$data := json (getv (printf "/v1/ipam/v4/pool/%s" .))}}
+  if ( net ~ {{$data.cidr}} ) then {
+{{- if $data.vxlan_mode}}
+    # Don't program VXLAN routes into the kernel - these are handled by Felix.
+    reject;
+  }
+{{- else if $data.ipip_mode}}{{if eq $data.ipip_mode "cross-subnet"}}
+    if defined(bgp_next_hop) && ( bgp_next_hop ~ {{$network}} ) then
+      krt_tunnel = "";                     {{- /* Destination in ipPool, mode is cross sub-net, route from-host on subnet, do not use IPIP */}}
+    else
+      krt_tunnel = "{{$data.ipip}}";       {{- /* Destination in ipPool, mode is cross sub-net, route from-host off subnet, set the tunnel (if IPIP not enabled, value will be "") */}}
+    accept;
+  } {{- else}}
+    krt_tunnel = "{{$data.ipip}}";         {{- /* Destination in ipPool, mode not cross sub-net, set the tunnel (if IPIP not enabled, value will be "") */}}
+    accept;
+  } {{- end}} {{- else}}
+    krt_tunnel = "{{$data.ipip}}";         {{- /* Destination in ipPool, mode field is not present, set the tunnel (if IPIP not enabled, value will be "") */}}
+    accept;
+  } {{- end}}
+{{end}}
+{{- end}}{{/* End of 'exists $network_key' */}}
+  accept;                                  {{- /* Destination is not in any ipPool, accept  */}}
+}
diff --git a/cluster/kube/lib/calico-bird.cfg.template b/cluster/kube/lib/calico-bird.cfg.template
new file mode 100644
index 0000000..8a79deb
--- /dev/null
+++ b/cluster/kube/lib/calico-bird.cfg.template
@@ -0,0 +1,164 @@
+# This is forked from bird.cfg.template from calico running on k0.hswaw.net on 2020/09/21.
+# Changed vs. upstream (C-f HSCLOUD):
+#  - set 'passive on' on 127.0.0.1 neighbors, used for estabilishing connectivity
+#    with metallb.
+# Generated by confd
+include "bird_aggr.cfg";
+include "bird_ipam.cfg";
+
+{{- $node_ip_key := printf "/host/%s/ip_addr_v4" (getenv "NODENAME")}}{{$node_ip := getv $node_ip_key}}
+{{- $router_id := getenv "CALICO_ROUTER_ID" ""}}
+
+{{- $node_name := getenv "NODENAME"}}
+
+router id {{if eq "hash" ($router_id) -}}
+	{{hashToIPv4 $node_name}};
+{{- else -}}
+	{{if ne "" ($router_id)}}{{$router_id}}{{else}}{{$node_ip}}{{end}};
+{{- end}}
+
+{{- define "LOGGING"}}
+{{- $node_logging_key := printf "/host/%s/loglevel" (getenv "NODENAME")}}
+{{- if exists $node_logging_key}}
+{{- $logging := getv $node_logging_key}}
+{{- if eq $logging "debug"}}
+  debug all;
+{{- else if ne $logging "none"}}
+  debug { states };
+{{- end}}
+{{- else if exists "/global/loglevel"}}
+{{- $logging := getv "/global/loglevel"}}
+{{- if eq $logging "debug"}}
+  debug all;
+{{- else if ne $logging "none"}}
+  debug { states };
+{{- end}}
+{{- else}}
+  debug { states };
+{{- end}}
+{{- end}}
+
+# Configure synchronization between routing tables and kernel.
+protocol kernel {
+  learn;             # Learn all alien routes from the kernel
+  persist;           # Don't remove routes on bird shutdown
+  scan time 2;       # Scan kernel routing table every 2 seconds
+  import all;
+  export filter calico_kernel_programming; # Default is export none
+  graceful restart;  # Turn on graceful restart to reduce potential flaps in
+                     # routes when reloading BIRD configuration.  With a full
+                     # automatic mesh, there is no way to prevent BGP from
+                     # flapping since multiple nodes update their BGP
+                     # configuration at the same time, GR is not guaranteed to
+                     # work correctly in this scenario.
+}
+
+# Watch interface up/down events.
+protocol device {
+{{- template "LOGGING"}}
+  scan time 2;    # Scan interfaces every 2 seconds
+}
+
+protocol direct {
+{{- template "LOGGING"}}
+  interface -"cali*", -"kube-ipvs*", "*"; # Exclude cali* and kube-ipvs* but
+                                          # include everything else.  In
+                                          # IPVS-mode, kube-proxy creates a
+                                          # kube-ipvs0 interface. We exclude
+                                          # kube-ipvs0 because this interface
+                                          # gets an address for every in use
+                                          # cluster IP. We use static routes
+                                          # for when we legitimately want to
+                                          # export cluster IPs.
+}
+
+{{if eq "" ($node_ip)}}# IPv4 disabled on this node.
+{{else}}{{$node_as_key := printf "/host/%s/as_num" (getenv "NODENAME")}}
+# Template for all BGP clients
+template bgp bgp_template {
+{{- $as_key := or (and (exists $node_as_key) $node_as_key) "/global/as_num"}}
+{{- $node_as_num := getv $as_key}}
+{{- template "LOGGING"}}
+  description "Connection to BGP peer";
+  local as {{$node_as_num}};
+  multihop;
+  gateway recursive; # This should be the default, but just in case.
+  import all;        # Import all routes, since we don't know what the upstream
+                     # topology is and therefore have to trust the ToR/RR.
+  export filter calico_export_to_bgp_peers;  # Only want to export routes for workloads.
+  source address {{$node_ip}};  # The local address we use for the TCP connection
+  add paths on;
+  graceful restart;  # See comment in kernel section about graceful restart.
+  connect delay time 2;
+  connect retry time 5;
+  error wait time 5,30;
+}
+
+# ------------- Node-to-node mesh -------------
+{{- $node_cid_key := printf "/host/%s/rr_cluster_id" (getenv "NODENAME")}}
+{{- $node_cluster_id := getv $node_cid_key}}
+{{if (json (getv "/global/node_mesh")).enabled}}
+{{range $host := lsdir "/host"}}
+{{$onode_as_key := printf "/host/%s/as_num" .}}
+{{$onode_ip_key := printf "/host/%s/ip_addr_v4" .}}{{if exists $onode_ip_key}}{{$onode_ip := getv $onode_ip_key}}
+{{$nums := split $onode_ip "."}}{{$id := join $nums "_"}}
+# For peer {{$onode_ip_key}}
+{{if eq $onode_ip ($node_ip) }}# Skipping ourselves ({{$node_ip}})
+{{else if ne "" $onode_ip}}protocol bgp Mesh_{{$id}} from bgp_template {
+  neighbor {{$onode_ip}} as {{if exists $onode_as_key}}{{getv $onode_as_key}}{{else}}{{getv "/global/as_num"}}{{end}};
+  {{- /*
+       Make the peering unidirectional. This avoids a race where
+       - peer A opens a connection and begins a graceful restart
+       - before the restart completes, peer B opens its connection
+       - peer A sees the new connection and aborts the graceful restart, causing a route flap.
+  */ -}}
+  {{if gt $onode_ip $node_ip}}
+  passive on; # Mesh is unidirectional, peer will connect to us. 
+  {{- end}}
+}{{end}}{{end}}{{end}}
+{{else}}
+# Node-to-node mesh disabled
+{{end}}
+
+
+# ------------- Global peers -------------
+{{if ls "/global/peer_v4"}}
+{{range gets "/global/peer_v4/*"}}{{$data := json .Value}}
+{{$nums := split $data.ip "."}}{{$id := join $nums "_"}}
+# For peer {{.Key}}
+{{- if eq $data.ip ($node_ip) }}
+# Skipping ourselves ({{$node_ip}})
+{{- else}}
+protocol bgp Global_{{$id}} from bgp_template {
+  {{if eq $data.ip ("127.0.0.1")}}passive on; # HSCLOUD {{end}}
+  neighbor {{$data.ip}} as {{$data.as_num}};
+{{- if and (eq $data.as_num $node_as_num) (ne "" ($node_cluster_id)) (ne $data.rr_cluster_id ($node_cluster_id))}}
+  rr client;
+  rr cluster id {{$node_cluster_id}};
+{{- end}}
+}
+{{- end}}
+{{end}}
+{{else}}# No global peers configured.{{end}}
+
+
+# ------------- Node-specific peers -------------
+{{$node_peers_key := printf "/host/%s/peer_v4" (getenv "NODENAME")}}
+{{if ls $node_peers_key}}
+{{range gets (printf "%s/*" $node_peers_key)}}{{$data := json .Value}}
+{{$nums := split $data.ip "."}}{{$id := join $nums "_"}}
+# For peer {{.Key}}
+{{- if eq $data.ip ($node_ip) }}
+# Skipping ourselves ({{$node_ip}})
+{{- else}}
+protocol bgp Node_{{$id}} from bgp_template {
+  neighbor {{$data.ip}} as {{$data.as_num}};
+{{- if and (eq $data.as_num $node_as_num) (ne "" ($node_cluster_id)) (ne $data.rr_cluster_id ($node_cluster_id))}}
+  rr client;
+  rr cluster id {{$node_cluster_id}};
+{{- end}}
+}
+{{- end}}
+{{end}}
+{{else}}# No node-specific peers configured.{{end}}
+{{end}}{{/* End of IPv4 enable check */}}
diff --git a/cluster/kube/lib/calico.libsonnet b/cluster/kube/lib/calico.libsonnet
index 28435ad..1e2d503 100644
--- a/cluster/kube/lib/calico.libsonnet
+++ b/cluster/kube/lib/calico.libsonnet
@@ -23,10 +23,10 @@
         local cfg = env.cfg,
         cfg:: {
             namespace: "kube-system",
-            version: "v3.4.0",
-            imageController: "quay.io/calico/kube-controllers:" + cfg.version,
-            imageCNI: "quay.io/calico/cni:" + cfg.version,
-            imageNode: "quay.io/calico/node:" + cfg.version,
+            version: "v3.14.0",
+            imageController: "calico/kube-controllers:" + cfg.version,
+            imageCNI: "calico/cni:" + cfg.version,
+            imageNode: "calico/node:" + cfg.version,
             // TODO(q3k): Separate etcd for calico
             etcd: {
                 endpoints: ["https://bc01n%02d.hswaw.net:2379" % n for n in std.range(1, 3)],
@@ -54,10 +54,12 @@
                 calico_backend: "bird",
                 veth_mtu: "1440",
 
+                typha_service_name: "none",
+
                 cni_network_config: |||
                    {
                      "name": "k8s-pod-network",
-                     "cniVersion": "0.3.0",
+                     "cniVersion": "0.3.1",
                      "plugins": [
                        {
                          "type": "calico",
@@ -66,6 +68,7 @@
                          "etcd_key_file": "__ETCD_KEY_FILE__",
                          "etcd_cert_file": "__ETCD_CERT_FILE__",
                          "etcd_ca_cert_file": "__ETCD_CA_CERT_FILE__",
+                         "datastore_type": "etcdv3",
                          "mtu": __CNI_MTU__,
                          "ipam": {
                              "type": "calico-ipam"
@@ -81,6 +84,10 @@
                          "type": "portmap",
                          "snat": true,
                          "capabilities": {"portMappings": true}
+                       },
+                       {
+                         "type": "bandwidth",
+                         "capabilities": {"bandwidth": true}
                        }
                      ]
                    }
@@ -116,13 +123,38 @@
                 {
                     apiGroups: [""],
                     resources: ["endpoints", "services"],
-                    verbs: ["watch", "list"],
+                    verbs: ["watch", "list", "get"],
+                },
+                {
+                    apiGroups: [""],
+                    resources: ["configmaps"],
+                    verbs: ["get"],
                 },
                 {
                     apiGroups: [""],
                     resources: ["nodes/status"],
+                    verbs: ["patch", "update"],
+                },
+                {
+                    apiGroups: ["networking.k8s.io"],
+                    resources: ["networkpolicies"],
+                    verbs: ["watch", "list"],
+                },
+                {
+                    apiGroups: [""],
+                    resources: ["pods", "namespaces", "serviceaccounts"],
+                    verbs: ["watch", "list"],
+                },
+                {
+                    apiGroups: [""],
+                    resources: ["pods/status"],
                     verbs: ["patch"],
                 },
+                {
+                    apiGroups: [""],
+                    resources: ["nodes"],
+                    verbs: ["get", "list", "watch"],
+                },
             ],
         },
 
@@ -138,8 +170,8 @@
             rules: [
                 {
                     apiGroups: [""],
-                    resources: ["pods", "nodes", "namespaces", "serviceaccounts"],
-                    verbs: ["watch", "list"],
+                    resources: ["nodes", "pods", "namespaces", "serviceaccounts"],
+                    verbs: ["watch", "list", "get"],
                 },
                 {
                     apiGroups: ["networking.k8s.io"],
@@ -198,6 +230,17 @@
             },
         },
 
+        # ConfigMap that holds overriden bird.cfg.template and bird_ipam.cfg.template.
+        calicoMetallbBird: kube.ConfigMap("calico-metallb-bird") {
+            metadata+: {
+                namespace: cfg.namespace,
+            },
+            data: {
+                "bird.cfg.template": (importstr "calico-bird.cfg.template"),
+                "bird_ipam.cfg.template": (importstr "calico-bird-ipam.cfg.template"),
+            },
+        },
+
         nodeDaemon: kube.DaemonSet("calico-node") {
             metadata+: {
                 namespace: cfg.namespace,
@@ -226,6 +269,7 @@
                             xtables_lock: kube.HostPathVolume("/run/xtables.lock"),
                             var_run_calico: kube.HostPathVolume("/var/run/calico"),
                             var_lib_calico: kube.HostPathVolume("/var/lib/calico"),
+                            bird_cfg_template: kube.ConfigMapVolume(env.calicoMetallbBird),
                         },
                         initContainers_: {
                             installCNI: kube.Container("install-cni") {
@@ -241,6 +285,7 @@
                                     CNI_MTU: kube.ConfigMapRef(env.cm, "veth_mtu"),
                                     CNI_NET_DIR: "/opt/cni/conf",
                                     SLEEP: "false",
+                                    KUBERNETES_NODE_NAME: { fieldRef: { fieldPath: "spec.nodeName" } },
                                 },
                                 volumeMounts_: {
                                     cni_bin: { mountPath: "/host/opt/cni/bin" },
@@ -253,12 +298,13 @@
                             calicoNode: kube.Container("calico-node") {
                                 image: cfg.imageNode,
                                 env_: {
+                                    DATASTORE_TYPE: "etcdv3",
                                     ETCD_ENDPOINTS: kube.ConfigMapRef(env.cm, "etcd_endpoints"),
                                     ETCD_CA_CERT_FILE: kube.ConfigMapRef(env.cm, "etcd_ca"),
                                     ETCD_KEY_FILE: kube.ConfigMapRef(env.cm, "etcd_key"),
                                     ETCD_CERT_FILE: kube.ConfigMapRef(env.cm, "etcd_cert"),
                                     CALICO_K8S_NODE_REF: kube.FieldRef("spec.nodeName"),
-                                    CALICO_NETWORK_BACKEND: kube.ConfigMapRef(env.cm, "calico_backend"),
+                                    CALICO_NETWORKING_BACKEND: kube.ConfigMapRef(env.cm, "calico_backend"),
                                     CLUSTER_TYPE: "k8s,bgp",
                                     IP: "autodetect",
                                     IP_AUTODETECTION_METHOD: "can-reach=185.236.240.1",
@@ -272,6 +318,7 @@
                                     FELIX_HEALTHENABLED: "true",
                                     FELIX_HEALTHHOST: "127.0.0.1",
                                     CALICO_ADVERTISE_CLUSTER_IPS: "10.10.12.0/24",
+                                    KUBERNETES_NODE_NAME: { fieldRef: { fieldPath: "spec.nodeName" } },
                                 },
                                 securityContext: {
                                     privileged: true,
@@ -280,10 +327,8 @@
                                     requests: { cpu: "250m" },
                                 },
                                 livenessProbe: {
-                                    httpGet: {
-                                        path: "/liveness",
-                                        port: 9099,
-                                        host: "127.0.0.1",
+                                    exec: {
+                                        command: ["/bin/calico-node", "-bird-live", "-felix-live"],
                                     },
                                     periodSeconds: 10,
                                     initialDelaySeconds: 10,
@@ -302,6 +347,16 @@
                                     var_lib_calico: { mountPath: "/var/lib/calico" },
                                     secrets: { mountPath: env.cm.secretPrefix },
                                 },
+                                volumeMounts+: [
+                                    { name: "bird-cfg-template",
+                                      mountPath: "/etc/calico/confd/templates/bird.cfg.template",
+                                      subPath: "bird.cfg.template"
+                                    },
+                                    { name: "bird-cfg-template",
+                                      mountPath: "/etc/calico/confd/templates/bird_ipam.cfg.template",
+                                      subPath: "bird_ipam.cfg.template"
+                                    },
+                                ],
                             },
                         },
                     },
diff --git a/cluster/kube/lib/cockroachdb.libsonnet b/cluster/kube/lib/cockroachdb.libsonnet
index 0b58180..8ebad52 100644
--- a/cluster/kube/lib/cockroachdb.libsonnet
+++ b/cluster/kube/lib/cockroachdb.libsonnet
@@ -53,6 +53,7 @@
 
             namespace: null,
             ownNamespace: cluster.cfg.namespace == null,
+            extraDNS: [],
         },
 
         namespaceName:: if cluster.cfg.namespace != null then cluster.cfg.namespace else name,
@@ -122,7 +123,7 @@
                     ] + [
                         "%s.cluster.local" % s.service.host
                         for s in cluster.servers
-                    ],
+                    ] + cluster.cfg.extraDNS,
                 },
             },
 
diff --git a/cluster/kube/lib/metrics.libsonnet b/cluster/kube/lib/metrics.libsonnet
index e11f5ef..fda3a59 100644
--- a/cluster/kube/lib/metrics.libsonnet
+++ b/cluster/kube/lib/metrics.libsonnet
@@ -1,4 +1,5 @@
 # Deploy a per-cluster Metrics Server setup.
+# These are Kubernetes metrics, not Prometheus/whatever.
 
 local kube = import "../../../kube/kube.libsonnet";
 
diff --git a/cluster/kube/lib/nginx.libsonnet b/cluster/kube/lib/nginx.libsonnet
index 9b9874f..510f851 100644
--- a/cluster/kube/lib/nginx.libsonnet
+++ b/cluster/kube/lib/nginx.libsonnet
@@ -31,7 +31,7 @@
             configuration: env.maps.make("nginx-configuration"),
             tcp: env.maps.make("tcp-services") {
                 data: {
-                    "22": "gerrit/gerrit:22"
+                    "22": "gerrit/gerrit:22",
                 }
             },
             udp: env.maps.make("udp-services"),
@@ -153,6 +153,20 @@
             },
         },
 
+        serviceGitea: kube.Service("ingress-nginx-gitea") {
+            metadata+: env.metadata,
+            target_pod:: env.deployment.spec.template,
+            spec+: {
+                type: "LoadBalancer",
+                loadBalancerIP: "185.236.240.60",
+                ports: [
+                    { name: "ssh", port: 22, targetPort: 222, protocol: "TCP" },
+                    { name: "http", port: 80, targetPort: 80, protocol: "TCP" },
+                    { name: "https", port: 443, targetPort: 443, protocol: "TCP" },
+                ],
+            },
+        },
+
         deployment: kube.Deployment("nginx-ingress-controller") {
             metadata+: env.metadata,
             spec+: {
@@ -210,6 +224,10 @@
                                     },
                                     runAsUser: 33,
                                 },
+                                resources: {
+                                    limits: { cpu: "2", memory: "4G" },
+                                    requests: { cpu: "1", memory: "1G" },
+                                },
                             },
                         },
                     },
diff --git a/cluster/kube/lib/prodvider.libsonnet b/cluster/kube/lib/prodvider.libsonnet
index 5805993..a4cb438 100644
--- a/cluster/kube/lib/prodvider.libsonnet
+++ b/cluster/kube/lib/prodvider.libsonnet
@@ -9,7 +9,7 @@
 
         cfg:: {
             namespace: "prodvider",
-            image: "registry.k0.hswaw.net/cluster/prodvider:1567256363-71a21c769369d013972d8dd0a71b83bee3e6848e",
+            image: "registry.k0.hswaw.net/q3k/prodvider:1601735780-d6c072a90e70b467a77039daebe602c77b4a84a1",
 
             apiEndpoint: error "API endpoint must be set",
 
diff --git a/cluster/kube/lib/registry.libsonnet b/cluster/kube/lib/registry.libsonnet
index 2df6da6..552d31b 100644
--- a/cluster/kube/lib/registry.libsonnet
+++ b/cluster/kube/lib/registry.libsonnet
@@ -73,9 +73,9 @@
                             blobdescriptor: "inmemory",
                         },
                         s3: {
-                            regionendpoint: "https://object.ceph-waw2.hswaw.net",
+                            regionendpoint: "https://object.ceph-waw3.hswaw.net",
                             bucket: "registry",
-                            region: "waw-hdd-redunant-2-object:default-placement",
+                            region: "waw-hdd-redunant-3-object:default-placement",
                         },
                     },
                     http: {
@@ -110,8 +110,8 @@
             },
         },
 
-        authVolumeClaim: kube.PersistentVolumeClaim("auth-token-storage") {
-            metadata+: env.metadata("auth-token-storage"),
+        authVolumeClaim: kube.PersistentVolumeClaim("auth-token-storage-3") {
+            metadata+: env.metadata("auth-token-storage-3"),
             spec+: {
                 storageClassName: cfg.storageClassName,
                 accessModes: [ "ReadWriteOnce" ],
@@ -157,6 +157,7 @@
                             { who: ["q3k", "informatic"], what: "go/svc/*" },
                             { who: ["q3k"], what: "bgpwtf/*" },
                             { who: ["q3k"], what: "devtools/*" },
+                            { who: ["q3k"], what: "games/factorio/*" },
                             { who: ["q3k", "informatic"], what: "cluster/*" },
                     ],
                     acl: [
@@ -171,7 +172,7 @@
                             comment: "Logged in users can query the catalog.",
                         },
                         {
-                            match: {account: ""},
+                            match: {account: "/.*/"},
                             actions: ["pull"],
                             comment: "Anyone can pull all images.",
                         },
@@ -314,7 +315,7 @@
 
         registryStorageUser: kube.CephObjectStoreUser("registry") {
             metadata+: {
-                namespace: "ceph-waw2",
+                namespace: "ceph-waw3",
             },
             spec: {
                 store: cfg.objectStorageName,
diff --git a/cluster/kube/lib/rook.libsonnet b/cluster/kube/lib/rook.libsonnet
index 68cf8a2..8f83d2d 100644
--- a/cluster/kube/lib/rook.libsonnet
+++ b/cluster/kube/lib/rook.libsonnet
@@ -3,12 +3,14 @@
 local kube = import "../../../kube/kube.libsonnet";
 local policies = import "../../../kube/policies.libsonnet";
 
+local oa = kube.OpenAPI;
+
 {
     Operator: {
         local env = self,
         local cfg = env.cfg,
         cfg:: {
-            image: "rook/ceph:v1.0.6",
+            image: "rook/ceph:v1.1.9",
             namespace: "rook-ceph-system",
         },
 
@@ -25,106 +27,135 @@
         policyInsecure: policies.AllowNamespaceInsecure(cfg.namespace),
 
         crds: {
-            cephclusters: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephCluster") {
+            # BUG: cannot control this because of:
+            # ERROR Error updating customresourcedefinitions cephclusters.ceph.rook.io: expected kind, but got map
+            # TODO(q3k): debug and fix kubecfg (it's _not_ just https://github.com/bitnami/kubecfg/issues/259 )
+            cephclusters:: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephCluster") {
                 spec+: {
                     additionalPrinterColumns: [
                         { name: "DataDirHostPath", type: "string", description: "Directory used on the K8s nodes", JSONPath: ".spec.dataDirHostPath" },
                         { name: "MonCount", type: "string", description: "Number of MONs", JSONPath: ".spec.mon.count" },
                         { name: "Age", type: "date", JSONPath: ".metadata.creationTimestamp" },
                         { name: "State", type: "string", description: "Current State", JSONPath: ".status.state" },
+                        { name: "Health", type: "string", description: "Ceaph Health", JSONPath: ".status.ceph.health" },
                     ],
-                    validation: {
-                        # Converted from official operator YAML
-                        "openAPIV3Schema": {
-                            "properties": {
-                                "spec": {
-                                    "properties": {
-                                        "cephVersion": {
-                                            "properties": {
-                                                "allowUnsupported": {
-                                                    "type": "boolean"
-                                                },
-                                                "image": {
-                                                    "type": "string"
-                                                },
-                                                "name": {
-                                                    "pattern": "^(luminous|mimic|nautilus)$",
-                                                    "type": "string"
-                                                }
-                                            }
-                                        },
-                                        "dashboard": {
-                                            "properties": {
-                                                "enabled": {
-                                                    "type": "boolean"
-                                                },
-                                                "urlPrefix": {
-                                                    "type": "string"
-                                                },
-                                                "port": {
-                                                    "type": "integer"
-                                                }
-                                            }
-                                        },
-                                        "dataDirHostPath": {
-                                            "pattern": "^/(\\S+)",
-                                            "type": "string"
-                                        },
-                                        "mon": {
-                                            "properties": {
-                                                "allowMultiplePerNode": {
-                                                    "type": "boolean"
-                                                },
-                                                "count": {
-                                                    "maximum": 9,
-                                                    "minimum": 1,
-                                                    "type": "integer"
-                                                },
-                                                "preferredCount": {
-                                                    "maximum": 9,
-                                                    "minimum": 0,
-                                                    "type": "integer"
-                                                }
-                                            },
-                                            "required": [
-                                                "count"
-                                            ]
-                                        },
-                                        "network": {
-                                            "properties": {
-                                                "hostNetwork": {
-                                                    "type": "boolean"
-                                                }
-                                            }
-                                        },
-                                        "storage": {
-                                            "properties": {
-                                                "nodes": {
-                                                    "items": {},
-                                                    "type": "array"
-                                                },
-                                                "useAllDevices": {},
-                                                "useAllNodes": {
-                                                    "type": "boolean"
-                                                }
-                                            }
-                                        }
+                    validation: oa.Validation(oa.Dict {
+                        spec: oa.Dict {
+                            annotations: oa.Any,
+                            cephVersion: oa.Dict {
+                                allowUnsupported: oa.Boolean,
+                                image: oa.String,
+                            },
+                            dashboard: oa.Dict {
+                                enabled: oa.Boolean,
+                                urlPrefix: oa.String,
+                                port: oa.Integer { minimum: 0, maximum: 65535 },
+                                ssl: oa.Boolean,
+                            },
+                            dataDirHostPath: oa.String { pattern: "^/(\\S+)" },
+                            skipUpgradeChecks: oa.Boolean,
+                            mon: oa.Dict {
+                                allowMultiplePerNode: oa.Boolean,
+                                count: oa.Integer { minimum: 0, maximum: 9 },
+                                preferredCount: oa.Integer { minimum: 0, maximum: 9 },
+                            },
+                            mgr: oa.Dict {
+                                modules: oa.Array(oa.Dict {
+                                    name: oa.String,
+                                    enabled: oa.Boolean,
+                                }),
+                            },
+                            network: oa.Dict {
+                                hostNetwork: oa.Boolean,
+                            },
+                            storage: oa.Dict {
+                                disruptionManagement: oa.Dict {
+                                    managePodBudgets: oa.Boolean,
+                                    osdMaintenanceTimeout: oa.Integer,
+                                    manageMachineDisruptionBudgets: oa.Boolean,
+                                },
+                                useAllNodes: oa.Boolean,
+                                nodes: oa.Array(oa.Dict {
+                                    name: oa.String,
+                                    config: oa.Dict {
+                                        metadataDevice: oa.String,
+                                        storeType: oa.String { pattern: "^(filestore|bluestore)$" },
+                                        databaseSizeMB: oa.String,
+                                        walSizeMB: oa.String,
+                                        journalSizeMB: oa.String,
+                                        osdsPerDevice: oa.String,
+                                        encryptedDevice: oa.String { pattern: "^(true|false)$" },
                                     },
-                                    "required": [
-                                        "mon"
-                                    ]
-                                }
-                            }
-                        }
-                    }
+                                    useAllDevices: oa.Boolean,
+                                    deviceFilter: oa.Any,
+                                    directories: oa.Array(oa.Dict {
+                                        path: oa.String,
+                                    }),
+                                    devices: oa.Array(oa.Dict {
+                                        name: oa.String,
+                                    }),
+                                    location: oa.Any,
+                                    resources: oa.Any,
+                                }),
+                                useAllDevices: oa.Boolean,
+                                deviceFilter: oa.Any,
+                                location: oa.Any,
+                                directories: oa.Array(oa.Dict {
+                                    path: oa.String,
+                                }),
+                                config: oa.Any,
+                                topologyAware: oa.Boolean,
+                            },
+                            monitoring: oa.Dict {
+                                enabled: oa.Boolean,
+                                rulesNamespace: oa.String,
+                            },
+                            rbdMirroring: oa.Dict {
+                                workers: oa.Integer,
+                            },
+                            placement: oa.Any,
+                            resources: oa.Any,
+                        },
+                    }),
                 },
             },
             cephfilesystems: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephFilesystem") {
                 spec+: {
                     additionalPrinterColumns: [
-                        { name: "MdsCount", type: "string", description: "Number of MDs", JSONPath: ".spec.metadataServer.activeCount" },
+                        { name: "ActiveMDS", type: "string", description: "Number of desired active MDS daemons", JSONPath: ".spec.metadataServer.activeCount" },
                         { name: "Age", type: "date", JSONPath: ".metadata.creationTimestamp" },
                     ],
+                    validation: oa.Validation(oa.Dict {
+                        spec: oa.Dict {
+                            metadataServer: oa.Dict {
+                                activeCount: oa.Integer,
+                                activeStandby: oa.Boolean,
+                                annotations: oa.Any,
+                                placement: oa.Any,
+                                resources: oa.Any,
+                            },
+                            metadataPool: oa.Dict {
+                                failureDomain: oa.String,
+                                replicated: oa.Dict {
+                                    size: oa.Integer,
+                                },
+                                erasureCoded: oa.Dict {
+                                    dataChunks: oa.Integer,
+                                    codingChunks: oa.Integer,
+                                },
+                            },
+                            dataPools: oa.Array(oa.Dict {
+                                failureDomain: oa.String,
+                                replicated: oa.Dict {
+                                    site: oa.Integer,
+                                    erasureCoded: oa.Dict {
+                                        dataChunks: oa.Integer,
+                                        codingChunks: oa.Integer,
+                                    },
+                                },
+                            })
+                        },
+                    }),
                 },
             },
             cephnfses: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephNFS") {
@@ -133,9 +164,52 @@
                         plural: "cephnfses",
                         shortNames: ["nfs"],
                     },
+                    validation: oa.Validation(oa.Dict {
+                        spec: oa.Dict {
+                            rados: oa.Dict {
+                                pool: oa.String,
+                                namespace: oa.String,
+                            },
+                            server: oa.Dict {
+                                active: oa.Integer,
+                                annotations: oa.Any,
+                                placement: oa.Any,
+                                resources: oa.Any,
+                            },
+                        },
+                    }),
                 },
             },
-            cephobjectstores: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephObjectStore"),
+            cephobjectstores: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephObjectStore") {
+                spec+: {
+                    validation: oa.Validation(oa.Dict {
+                        spec: oa.Dict {
+                            gateway: oa.Dict {
+                                type: oa.String,
+                                sslCertificateRef: oa.Any,
+                                port: oa.Integer,
+                                securePort: oa.Any,
+                                instances: oa.Integer,
+                                annotations: oa.Any,
+                                placement: oa.Any,
+                                resources: oa.Any,
+                            },
+                            local poolDef = oa.Dict {
+                                failureDomain: oa.String,
+                                replicated: oa.Dict {
+                                    size: oa.Integer,
+                                },
+                                erasureCoded: oa.Dict {
+                                    dataChunks: oa.Integer,
+                                    codingChunks: oa.Integer,
+                                },
+                            },
+                            metadataPool: poolDef,
+                            dataPool: poolDef,
+                        },
+                    }),
+                },
+            },
             cephobjectstoreusers: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephObjectStoreUser"),
             cephblockpools: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephBlockPool"),
             volumes: kube.CustomResourceDefinition("rook.io", "v1alpha2", "Volume") {
@@ -145,10 +219,41 @@
                     },
                 },
             },
+            objectbuckets: kube.CustomResourceDefinition("objectbucket.io", "v1alpha1", "ObjectBucket") {
+                spec+: {
+                    names+: {
+                        shortNames: ["ob", "obs"],
+                    },
+                    scope: "Cluster",
+                    subresources: { status: {} },
+                },
+            },
+            objectbucketclaims: kube.CustomResourceDefinition("objectbucket.io", "v1alpha1", "ObjectBucketClaim") {
+                spec+: {
+                    names+: {
+                        shortNames: ["obc", "obcs"],
+                    },
+                    subresources: { status: {} },
+                },
+            },
         },
 
-        sa: kube.ServiceAccount("rook-ceph-system") {
-            metadata+: env.metadata,
+        sa: {
+            system: kube.ServiceAccount("rook-ceph-system") {
+                metadata+: env.metadata,
+            },
+            csiCephfsPlugin: kube.ServiceAccount("rook-csi-cephfs-plugin-sa") {
+                metadata+: env.metadata,
+            },
+            csiCephfsProvisioner: kube.ServiceAccount("rook-csi-cephfs-provisioner-sa") {
+                metadata+: env.metadata,
+            },
+            csiRbdPlugin: kube.ServiceAccount("rook-csi-rbd-plugin-sa") {
+                metadata+: env.metadata,
+            },
+            csiRbdProvisioner: kube.ServiceAccount("rook-csi-rbd-provisioner-sa") {
+                metadata+: env.metadata,
+            },
         },
 
         crs: {
@@ -183,7 +288,7 @@
                     {
                         apiGroups: ["storage.k8s.io"],
                         resources: ["storageclasses"],
-                        verbs: ["get", "list", "watch", "create", "update", "delete"],
+                        verbs: ["get", "list", "watch"],
                     },
                     {
                         apiGroups: ["batch"],
@@ -200,46 +305,356 @@
                         resources: ["*"],
                         verbs: ["*"],
                     },
+                    {
+                        apiGroups: ["policy", "apps"],
+                        resources: ["poddisruptionbudgets", "deployments"],
+                        verbs: ["*"],
+                    },
                 ],
             },
+
+            // Upstream rook uses split ClusterRoles, with the 'main' role (eg rook-ceph-cluster-mgmt)
+            // using aggregationRules to point to a '-rules' role (eg rook-ceph-cluster-mgmt-rules) which
+            // contains the actual role rules. This was done to permit for a bettr upgrade experience on
+            // systems that only allow for a recreation of a clusterroles (see https://github.com/rook/rook/issues/2634
+            // for more background information).
+            // We do not use this split because our update mechanism is not broken. However, it seems
+            // that Rook started to use these split rules for other reasons, too. For instance, the
+            // mgr-cluster role in upstream not only aggregates its equivalent -rules role, but also
+            // the rook-ceph-object-bucket role. As such, we split mgr-cluster as they do in upstream.
+            // In the future, we may split the rest of the roles in order to stay consisdent with upsteam.
+
             mgrCluster: kube.ClusterRole("rook-ceph-mgr-cluster") {
                 metadata+: env.metadata { namespace:: null },
+                aggregationRule: {
+                    clusterRoleSelectors: [
+                        { matchLabels: { "rbac.ceph.rook.io/aggregate-to-rook-ceph-mgr-cluster": "true" }},
+                    ],
+                },
+            },
+            mgrClusterRules: kube.ClusterRole("rook-ceph-mgr-cluster-rules") {
+                metadata+: env.metadata {
+                    namespace:: null,
+                    labels+: {
+                        "rbac.ceph.rook.io/aggregate-to-rook-ceph-mgr-cluster": "true",
+                    },
+                },
                 rules: [
                     {
                         apiGroups: [""],
                         resources: ["configmaps", "nodes", "nodes/proxy"],
                         verbs: ["get", "list", "watch"],
                     },
+                    {
+                        apiGroups: [""],
+                        resources: ["events"],
+                        verbs: ["create", "patch", "list", "get", "watch"],
+                    },
                 ]
             },
+            objectBucket: kube.ClusterRole("rook-ceph-object-bucket") {
+                metadata+: env.metadata {
+                    namespace:: null,
+                    labels+: {
+                        "rbac.ceph.rook.io/aggregate-to-rook-ceph-mgr-cluster": "true",
+                    },
+                },
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["secrets", "configmaps"],
+                        verbs: ["*"],
+                    },
+                    {
+                        apiGroups: ["storage.k8s.io"],
+                        resources: ["storageclasses"],
+                        verbs: ["get", "list", "watch"],
+                    },
+                    {
+                        apiGroups: ["objectbucket.io"],
+                        resources: ["*"],
+                        verbs: ["*"],
+                    },
+                ],
+            },
+
+            cephfsCSINodeplugin: kube.ClusterRole("cephfs-csi-nodeplugin") {
+                metadata+: env.metadata { namespace:: null },
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["nodes"],
+                        verbs: ["get", "list", "update"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["namespaces"],
+                        verbs: ["get", "list"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["persistentvolumes"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: ["storage.k8s.io"],
+                        resources: ["volumeattachments"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["configmaps"],
+                        verbs: ["get", "list"],
+                    },
+                ],
+            },
+
+            cephfsExternalProvisionerRunner: kube.ClusterRole("cephfs-external-provisioner-runner") {
+                metadata+: env.metadata { namespace:: null },
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["secrets"],
+                        verbs: ["get", "list"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["persistentvolumes"],
+                        verbs: ["get", "list", "watch", "create", "update", "delete"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["persistentvolumeclaims"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: ["storage.k8s.io"],
+                        resources: ["storageclasses"],
+                        verbs: ["get", "list", "watch"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["events"],
+                        verbs: ["list", "watch", "create", "update", "patch"],
+                    },
+                    {
+                        apiGroups: ["storage.k8s.io"],
+                        resources: ["volumeattachments"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["nodes"],
+                        verbs: ["get", "list", "watch"],
+                    },
+                ],
+            },
+
+            rbdCSINodeplugin: kube.ClusterRole("rbd-csi-nodeplugin") {
+                metadata+: env.metadata { namespace:: null },
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["secrets"],
+                        verbs: ["get", "list"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["nodes"],
+                        verbs: ["get", "list", "update"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["namespaces"],
+                        verbs: ["get", "list"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["persistentvolumes"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: ["storage.k8s.io"],
+                        resources: ["volumeattachments"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["configmaps"],
+                        verbs: ["get", "list"],
+                    },
+                ],
+            },
+
+            rbdExternalProvisionerRunner: kube.ClusterRole("rbd-external-provisioner-runner") {
+                metadata+: env.metadata { namespace:: null },
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["secrets"],
+                        verbs: ["get", "list"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["persistentvolumes"],
+                        verbs: ["get", "list", "watch", "create", "update", "delete"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["persistentvolumeclaims"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: ["storage.k8s.io"],
+                        resources: ["volumeattachments"],
+                        verbs: ["get", "list", "watch", "update"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["nodes"],
+                        verbs: ["get", "list", "watch"],
+                    },
+                    {
+                        apiGroups: ["storage.k8s.io"],
+                        resources: ["storageclasses"],
+                        verbs: ["get", "list", "watch"]
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["events"],
+                        verbs: ["list", "watch", "create", "update", "patch"],
+                    },
+                    {
+                        apiGroups: ["snapshot.storage.k8s.io"],
+                        resources: ["volumesnapshotcontents"],
+                        verbs: ["create", "get", "list", "watch", "update", "delete"],
+                    },
+                    {
+                        apiGroups: ["snapshot.storage.k8s.io"],
+                        resources: ["volumesnapshotclasses"],
+                        verbs: ["get", "list", "watch"],
+                    },
+                    {
+                        apiGroups: ["apiextensions.k8s.io"],
+                        resources: ["customresourcedefinitions"],
+                        verbs: ["create", "list", "watch", "delete", "get", "update"],
+                    },
+                    {
+                        apiGroups: ["snapshot.storage.k8s.io"],
+                        resources: ["volumesnapshots/status"],
+                        verbs: ["update"],
+                    },
+                ],
+            },
         },
 
-        crb: kube.ClusterRoleBinding("ceph-rook-global") {
-            metadata+: env.metadata { namespace:: null },
-            roleRef_: env.crs.global,
-            subjects_: [env.sa],
+        crbs: {
+            global: kube.ClusterRoleBinding("ceph-rook-global") {
+                metadata+: env.metadata { namespace:: null },
+                roleRef_: env.crs.global,
+                subjects_: [env.sa.system],
+            },
+            objectBucket: kube.ClusterRoleBinding("rook-ceph-object-bucket") {
+                metadata+: env.metadata { namespace:: null },
+                roleRef_: env.crs.objectBucket,
+                subjects_: [env.sa.system],
+            },
+            cephfsCSINodeplugin: kube.ClusterRoleBinding("cepfs-csi-nodeplugin") {
+                metadata+: env.metadata { namespace:: null },
+                roleRef_: env.crs.cephfsCSINodeplugin,
+                subjects_: [env.sa.csiCephfsPlugin],
+            },
+            cephfsCSIProvisioner: kube.ClusterRoleBinding("cephfs-csi-provisioner") {
+                metadata+: env.metadata { namespace:: null },
+                roleRef_: env.crs.cephfsExternalProvisionerRunner,
+                subjects_: [env.sa.csiCephfsProvisioner],
+            },
+            rbdCSINodeplugin: kube.ClusterRoleBinding("rbd-csi-nodeplugin") {
+                metadata+: env.metadata { namespace:: null },
+                roleRef_: env.crs.rbdCSINodeplugin,
+                subjects_: [env.sa.csiRbdPlugin],
+            },
+            rbdCSIProvisioner: kube.ClusterRoleBinding("rbd-csi-provisioner") {
+                metadata+: env.metadata { namespace:: null },
+                roleRef_: env.crs.rbdExternalProvisionerRunner,
+                subjects_: [env.sa.csiRbdProvisioner],
+            },
         },
 
-        role: kube.Role("ceph-rook-system") {
-            metadata+: env.metadata,
-            rules: [
-                {
-                    apiGroups: [""],
-                    resources: ["pods", "configmaps"],
-                    verbs: ["get", "list", "watch", "patch", "create", "update", "delete"],
-                },
-                {
-                    apiGroups: ["apps"],
-                    resources: ["daemonsets"],
-                    verbs: ["get", "list", "watch", "create", "update", "delete"],
-                },
-            ],
+        roles: {
+            system: kube.Role("ceph-rook-system") {
+                metadata+: env.metadata,
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["pods", "configmaps", "services"],
+                        verbs: ["get", "list", "watch", "patch", "create", "update", "delete"],
+                    },
+                    {
+                        apiGroups: ["apps"],
+                        resources: ["deployments", "statefulsets", "daemonsets"],
+                        verbs: ["get", "list", "watch", "create", "update", "delete"],
+                    },
+                ],
+            },
+            cephfsExternalProvisioner: kube.Role("cephfs-external-provisioner-cfg") {
+                metadata+: env.metadata,
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["endpoints"],
+                        verbs: ["get", "watch", "list", "delete", "update", "create"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["configmaps"],
+                        verbs: ["get", "list", "create", "delete"],
+                    },
+                    {
+                        apiGroups: ["coordination.k8s.io"],
+                        resources: ["leases"],
+                        verbs: ["get" ,"watch", "list", "delete", "update", "create"],
+                    },
+                ],
+            },
+            rbdExternalProvisioner: kube.Role("rbd-external-provisioner-cfg") {
+                metadata+: env.metadata,
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["endpoints"],
+                        verbs: ["get", "watch", "list", "delete", "update", "create"],
+                    },
+                    {
+                        apiGroups: [""],
+                        resources: ["configmaps"],
+                        verbs: ["get", "list", "watch", "create", "delete"],
+                    },
+                    {
+                        apiGroups: ["coordination.k8s.io"],
+                        resources: ["leases"],
+                        verbs: ["get" ,"watch", "list", "delete", "update", "create"],
+                    },
+                ],
+            },
         },
 
-        rb: kube.RoleBinding("ceph-rook-system") {
-            metadata+: env.metadata,
-            roleRef_: env.role,
-            subjects_: [env.sa],
+        rbs: {
+            system: kube.RoleBinding("ceph-rook-system") {
+                metadata+: env.metadata,
+                roleRef_: env.roles.system,
+                subjects_: [env.sa.system],
+            },
+            cephfsCSIProvisioner: kube.RoleBinding("cephfs-csi-provisioner-role-cfg") {
+                metadata+: env.metadata,
+                roleRef_: env.roles.cephfsExternalProvisioner,
+                subjects_: [env.sa.csiCephfsProvisioner],
+            },
+            rbdCSIProvisioner: kube.RoleBinding("rbd-csi-provisioner-role-cfg") {
+                metadata+: env.metadata,
+                roleRef_: env.roles.rbdExternalProvisioner,
+                subjects_: [env.sa.csiRbdProvisioner],
+            },
         },
 
         operator: kube.Deployment("rook-ceph-operator") {
@@ -247,7 +662,7 @@
             spec+: {
                 template+: {
                     spec+: {
-                        serviceAccountName: env.sa.metadata.name,
+                        serviceAccountName: env.sa.system.metadata.name,
                         containers_: {
                             operator: kube.Container("rook-ceph-operator") {
                                 image: cfg.image,
@@ -269,6 +684,7 @@
                                     NODE_NAME: kube.FieldRef("spec.nodeName"),
                                     POD_NAME: kube.FieldRef("metadata.name"),
                                     POD_NAMESPACE: kube.FieldRef("metadata.namespace"),
+                                    ROOK_CSI_KUBELET_DIR_PATH: "/var/lib/kubernetes"
                                 },
                             },
                         },
@@ -304,6 +720,9 @@
             mgr: kube.ServiceAccount("rook-ceph-mgr") {
                 metadata+: cluster.metadata,
             },
+            cmdReporter: kube.ServiceAccount("rook-ceph-cmd-reporter") {
+                metadata+: cluster.metadata,
+            },
         },
 
         roles: {
@@ -337,6 +756,16 @@
                     },
                 ],
             },
+            cmdReporter: kube.Role(cluster.name("cmd-reporter")) {
+                metadata+: cluster.metadata,
+                rules: [
+                    {
+                        apiGroups: [""],
+                        resources: ["pods", "configmaps"],
+                        verbs: ["get", "list", "watch", "create", "update", "delete"],
+                    },
+                ],
+            },
             mgrSystem: kube.ClusterRole(cluster.name("mgr-system")) {
                 metadata+: cluster.metadata { namespace:: null },
                 rules: [
@@ -357,9 +786,10 @@
             },
             for el in [
                 // Allow Operator SA to perform Cluster Mgmt in this namespace.
-                { name: "cluster-mgmt", role: operator.crs.clusterMgmt, sa: operator.sa },
+                { name: "cluster-mgmt", role: operator.crs.clusterMgmt, sa: operator.sa.system },
                 { name: "osd", role: cluster.roles.osd, sa: cluster.sa.osd },
                 { name: "mgr", role: cluster.roles.mgr, sa: cluster.sa.mgr },
+                { name: "cmd-reporter", role: cluster.roles.cmdReporter, sa: cluster.sa.cmdReporter },
                 { name: "mgr-cluster", role: operator.crs.mgrCluster, sa: cluster.sa.mgr },
             ]
         ],
diff --git a/cluster/nix/default.nix b/cluster/nix/default.nix
index 48690d4..a5f5082 100644
--- a/cluster/nix/default.nix
+++ b/cluster/nix/default.nix
@@ -1,8 +1,8 @@
 let
   pkgs = import (fetchGit {
-    name = "nixos-unstable-2020-02-12";
+    name = "nixos-unstable-2020-08-22";
     url = https://github.com/nixos/nixpkgs-channels/;
-    rev = "a21c2fa3ea2b88e698db6fc151d9c7259ae14d96";
+    rev = "c59ea8b8a0e7f927e7291c14ea6cd1bd3a16ff38";
   });
 
   cfg = {
diff --git a/cluster/nix/module-base.nix b/cluster/nix/modules/base.nix
similarity index 93%
rename from cluster/nix/module-base.nix
rename to cluster/nix/modules/base.nix
index 266c145..84530c3 100644
--- a/cluster/nix/module-base.nix
+++ b/cluster/nix/modules/base.nix
@@ -1,6 +1,6 @@
 { config, pkgs, lib, ... }:
 
-with (( import ./defs-cluster-k0.nix ) config.networking.hostName);
+with (( import ../defs-cluster-k0.nix ) config.networking.hostName);
 
 rec {
   system.stateVersion = machine.stateVersion;
@@ -42,11 +42,14 @@
   # Otherwise fetchGit nixpkgs pin fails.
   systemd.services.nixos-upgrade.path = [ pkgs.git ];
 
+  # Use Chrony instead of systemd-timesyncd
+  services.chrony.enable = true;
+
   # Enable the OpenSSH daemon.
   services.openssh.enable = true;
   users.users.root.openssh.authorizedKeys.keys = [
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD4VJXAXEHEXZk2dxNwehneuJcEGkfXG/U7z4fO79vDVIENdedtXQUyLyhZJc5RTEfHhQj66FwIqzl7mzBHd9x9PuDp6QAYXrkVNMj48s6JXqZqBvF6H/weRqFMf4a2TZv+hG8D0kpvmLheCwWAVRls7Jofnp/My+yDd57GMdsbG/yFEf6WPMiOnA7hxdSJSVihCsCSw2p8PD4GhBe8CVt7xIuinhutjm9zYBjV78NT8acjDUfJh0B1ODTjs7nuW1CC4jybSe2j/OU3Yczj4AxRxBNWuFxUq+jBo9BfpbKLh+Tt7re+zBkaicM77KM/oV6943JJxgHNBBOsv9scZE7 q3k@amnesia"
-    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPm+KopMxs7QfATTKJBjCSKwttslx1u3dHl7tuppwN/4 q3k@paranoia"
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG599UildOrAq+LIOQjKqtGMwjgjIxozI1jtQQRKHtCP q3k@mimeomia"
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQb3YQoiYFZLKwvHYKbu1bMqzNeDCAszQhAe1+QI5SLDOotclyY/vFmOReZOsmyMFl71G2d7d+FbYNusUnNNjTxRYQ021tVc+RkMdLJaORRURmQfEFEKbai6QSFTwErXzuoIzyEPK0lbsQuGgqT9WaVnRzHJ2Q/4+qQbxAS34PuR5NqEkmn4G6LMo3OyJ5mwPkCj9lsqz4BcxRaMWFO3mNcwGDfSW+sqgc3E8N6LKrTpZq3ke7xacpQmcG5DU9VO+2QVPdltl9jWbs3gXjmF92YRNOuKPVfAOZBBsp8JOznfx8s9wDgs7RwPmDpjIAJEyoABqW5hlXfqRbTnfnMvuR informatic@InformaticPC"
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGkMgEVwQM8yeuFUYL2TwlJIq9yUNBmHnwce46zeL2PK2CkMz7sxT/om7sp/K5XDiqeD05Nioe+Dr3drP6B8uI33S5NgxPIfaqQsRS+CBEgk6cqFlcdlKETU/DT+/WsdoO173n7mgGeafPInEuQuGDUID0Fl099kIxtqfAhdeZFMM6/szAZEZsElLJ8K6dp1Ni/jmnXCZhjivZH3AZUlnqrmtDG7FY1bgcOfDXAal45LItughGPtrdiigXe9DK2fW3+9DBZZduh5DMJTNlphAZ+nfSrbyHVKUg6WsgMSprur4KdU47q1QwzqqvEj75JcdP1jOWoZi4F6VJDte9Wb9lhD1jGgjxY9O6Gs4CH35bx15W7CN9hgNa0C8NbPJe/fZYIeMZmJ1m7O2xmnYwP8j+t7RNJWu7Pa3Em4mOEXvhBF07Zfq+Ye/4SluoRgADy5eII2x5fFo5EBhInxK0/X8wF6XZvysalVifoCh7T4Edejoi91oAxFgYAxbboXGlod0eEHIi2hla8SM9+IBHOChmgawKBYp2kzAJyAmHNBF+Pah9G4arVCj/axp/SJZDZbJQoI7UT/fJzEtvlb5RWrHXRq+y6IvjpUq4pzpDWW04+9UMqEEXRmhWOakHfEVM9rN8h3aJBflLUBBnh0Z/hVsKNh8bCRHaKtah8TrD9i+wMw== patryk.jakuszew@gmail.com"
     "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC33naG1ptCvUcRWX9cj9wXM1nW1lyQC4SvMJzWlr9aMD96O8hQ2JMkuIUgUJvorAY02QRplQ2BuoVoVkdkzwjMyi1bL3OdgcKo7Z1yByClGTTocqNJYY0lcUb6EJH8+6e6F9ydrQlSxNzL1uCaA7phZr+yPcmAmWbSfioXn98yXNkE0emHxzJv/nypJY56sDCMC2IXDRd8L2goDtPwgPEW7bWfAQdIFMJ75xOidZOTxJ8eqyXLw/kxY5UlyX66jdoYz1sE5XUHuoQl1AOG9UdlMo0aMhUvP4pX5l7r7EnA9OttKMFB3oWqkVK/R6ynZ52YNOU5BZ9V+Ppaj34W0xNu+p0mbHcCtXYCTrf/OU0hcZDbDaNTjs6Vtcm2wYw9iAKX7Tex+eOMwUwlrlcyPNRV5BTot7lGNYfauHCSIuWJKN4NhCLR/NtVNh4/94eKkPTwJsY6XqDcS7q49wPAs4DAH7BJgsbHPOqygVHrY0YYEfz3Pj0HTxJHQMCP/hQX4fXEGt0BjgoVJbXPAQtPyeg0JuxiUg+b4CgVVfQ6R060MlM1BZzhmh+FY5MJH6nJppS0aHYCvSg8Z68NUlCPKy0jpcyfuAIWQWwSGG1O010WShQG2ELsvNdg5/4HVdCGNl5mmoom6JOd72FOZyQlHDFfeQUQRn9HOeCq/c51rK99SQ== bartek@IHM"
diff --git a/cluster/nix/module-kubelet.nix b/cluster/nix/modules/kubelet.nix
similarity index 100%
rename from cluster/nix/module-kubelet.nix
rename to cluster/nix/modules/kubelet.nix
diff --git a/cluster/nix/module-kubernetes.nix b/cluster/nix/modules/kubernetes.nix
similarity index 94%
rename from cluster/nix/module-kubernetes.nix
rename to cluster/nix/modules/kubernetes.nix
index 420b32d..10560cd 100644
--- a/cluster/nix/module-kubernetes.nix
+++ b/cluster/nix/modules/kubernetes.nix
@@ -1,20 +1,20 @@
 { config, pkgs, lib, ... }:
 
-with (( import ./defs-cluster-k0.nix ) config.networking.hostName);
+with (( import ../defs-cluster-k0.nix ) config.networking.hostName);
 let
   # Pin for k8s packages. This is so that upagrading the system will not upgrade the k8s control or data planes.
   k8spkgs = import (fetchGit {
-    # Now at 1.14.3
-    name = "nixos-unstable-2019-06-17";
+    # Now at 1.15.4
+    name = "nixos-unstable-2019-09-18";
     url = https://github.com/nixos/nixpkgs-channels/;
-    rev = "415e8e5820b7825fb74a6c7986bf6af725227eaa";
+    rev = "b21a3356f01b59992432a907f17e66abc77f17a0";
   }) {};
   # Pin for kubelet
   k8spkgsKubelet = import (fetchGit {
-    # Now at 1.14.3
-    name = "nixos-unstable-2019-06-17";
+    # Now at 1.15.4
+    name = "nixos-unstable-2019-09-18";
     url = https://github.com/nixos/nixpkgs-channels/;
-    rev = "415e8e5820b7825fb74a6c7986bf6af725227eaa";
+    rev = "b21a3356f01b59992432a907f17e66abc77f17a0";
   }) {};
 
 in rec {
@@ -27,7 +27,7 @@
 
   imports =
     [
-      ./module-kubelet.nix
+      ./kubelet.nix
     ];
 
   # List services that you want to enable:
@@ -55,6 +55,7 @@
     127.0.0.1 ${k8sapi}
   '';
 
+  security.acme.acceptTerms = true;
   security.acme.certs = {
     host = {
       email = acmeEmail;
diff --git a/cluster/nix/provision.nix b/cluster/nix/provision.nix
index dbe697b..70240f6 100644
--- a/cluster/nix/provision.nix
+++ b/cluster/nix/provision.nix
@@ -7,8 +7,8 @@
     value = super.nixos ({ config, pkgs, ... }: {
       networking.hostName = machine.name;
       imports = [
-        ./module-base.nix
-        ./module-kubernetes.nix
+        ./modules/base.nix
+        ./modules/kubernetes.nix
       ];
     });
   }) machines);
diff --git a/cluster/prodaccess/BUILD.bazel b/cluster/prodaccess/BUILD.bazel
index 5124ffc..6c72082 100644
--- a/cluster/prodaccess/BUILD.bazel
+++ b/cluster/prodaccess/BUILD.bazel
@@ -3,6 +3,7 @@
 go_library(
     name = "go_default_library",
     srcs = [
+        "hspki.go",
         "kubernetes.go",
         "prodaccess.go",
     ],
@@ -11,6 +12,7 @@
     deps = [
         "//cluster/certs:go_default_library",
         "//cluster/prodvider/proto:go_default_library",
+        "//go/pki:go_default_library",
         "@com_github_golang_glog//:go_default_library",
         "@org_golang_google_grpc//:go_default_library",
         "@org_golang_google_grpc//credentials:go_default_library",
diff --git a/cluster/prodaccess/hspki.go b/cluster/prodaccess/hspki.go
new file mode 100644
index 0000000..2fcfaf0
--- /dev/null
+++ b/cluster/prodaccess/hspki.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+	"io/ioutil"
+	"os"
+
+	"github.com/golang/glog"
+
+	pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
+	"code.hackerspace.pl/hscloud/go/pki"
+)
+
+func useHSPKIKeys(keys *pb.HSPKIKeys) {
+	path, err := pki.DeveloperCredentialsLocation()
+	err = os.MkdirAll(path, 0700)
+	if err != nil {
+		glog.Exitf("mkdir %q: %v", path, err)
+	}
+
+	for _, el := range []struct {
+		target string
+		data   []byte
+	}{
+		{path + "/ca.crt", keys.Ca},
+		{path + "/tls.crt", keys.Cert},
+		{path + "/tls.key", keys.Key},
+	} {
+		err := ioutil.WriteFile(el.target, el.data, 400)
+		if err != nil {
+			glog.Exitf("Failed to write %q: %v", el.target, err)
+		}
+	}
+}
diff --git a/cluster/prodaccess/prodaccess.go b/cluster/prodaccess/prodaccess.go
index e0e8ec2..1153bab 100644
--- a/cluster/prodaccess/prodaccess.go
+++ b/cluster/prodaccess/prodaccess.go
@@ -99,6 +99,9 @@
 	}
 
 	useKubernetesKeys(res.KubernetesKeys)
+	fmt.Printf("-> Kubernetes credentials installed\n")
+	useHSPKIKeys(res.HspkiKeys)
+	fmt.Printf("-> HSPKI credentials installed\n")
 
 	return true
 }
diff --git a/cluster/prodvider/BUILD.bazel b/cluster/prodvider/BUILD.bazel
index 14690b7..c15ab66 100644
--- a/cluster/prodvider/BUILD.bazel
+++ b/cluster/prodvider/BUILD.bazel
@@ -5,6 +5,7 @@
     name = "go_default_library",
     srcs = [
         "certs.go",
+        "hspki.go",
         "kubernetes.go",
         "main.go",
         "service.go",
@@ -15,6 +16,7 @@
         "//cluster/prodvider/proto:go_default_library",
         "@com_github_cloudflare_cfssl//config:go_default_library",
         "@com_github_cloudflare_cfssl//csr:go_default_library",
+        "@com_github_cloudflare_cfssl//helpers:go_default_library",
         "@com_github_cloudflare_cfssl//signer:go_default_library",
         "@com_github_cloudflare_cfssl//signer/local:go_default_library",
         "@com_github_golang_glog//:go_default_library",
@@ -59,6 +61,6 @@
     image = ":runtime",
     format = "Docker",
     registry = "registry.k0.hswaw.net",
-    repository = "cluster/prodvider",
+    repository = "q3k/prodvider",
     tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}",
 )
diff --git a/cluster/prodvider/certs.go b/cluster/prodvider/certs.go
index bed0e48..309af1f 100644
--- a/cluster/prodvider/certs.go
+++ b/cluster/prodvider/certs.go
@@ -57,6 +57,7 @@
 				OU: signerCert.Subject.OrganizationalUnit[0],
 			},
 		},
+		Hosts: []string{flagProdviderCN},
 	}
 
 	g := &csr.Generator{
@@ -68,7 +69,7 @@
 
 func (p *prodvider) makeSelfCertificate(csr []byte) ([]byte, error) {
 	req := signer.SignRequest{
-		Hosts:   []string{},
+		Hosts:   []string{flagProdviderCN},
 		Request: string(csr),
 		Profile: "server",
 	}
diff --git a/cluster/prodvider/hspki.go b/cluster/prodvider/hspki.go
new file mode 100644
index 0000000..e747889
--- /dev/null
+++ b/cluster/prodvider/hspki.go
@@ -0,0 +1,103 @@
+package main
+
+import (
+	"encoding/pem"
+	"fmt"
+	"time"
+
+	"github.com/cloudflare/cfssl/config"
+	"github.com/cloudflare/cfssl/csr"
+	"github.com/cloudflare/cfssl/helpers"
+	"github.com/cloudflare/cfssl/signer"
+	"github.com/cloudflare/cfssl/signer/local"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
+)
+
+// hspkiSigner returns a cfssl signer (CA) for HSPKI, by loading the CA
+// cert/key from Kubernetes.
+func (p *prodvider) hspkiSigner() (*local.Signer, error) {
+	policy := &config.Signing{
+		Profiles: map[string]*config.SigningProfile{
+			"client-server": &config.SigningProfile{
+				Usage:        []string{"signing", "key encipherment", "server auth", "client auth"},
+				ExpiryString: "30d",
+			},
+		},
+		Default: config.DefaultConfig(),
+	}
+
+	secret, err := p.k8s.CoreV1().Secrets("cert-manager").Get("pki-selfsigned-cert", metav1.GetOptions{})
+	if err != nil {
+		return nil, fmt.Errorf("hspki secret get failed: %w", err)
+	}
+
+	parsedCa, err := helpers.ParseCertificatePEM(secret.Data["tls.crt"])
+	if err != nil {
+		return nil, fmt.Errorf("when parsing tls.crt: %w", err)
+	}
+
+	priv, err := helpers.ParsePrivateKeyPEMWithPassword(secret.Data["tls.key"], nil)
+	if err != nil {
+		return nil, fmt.Errorf("when parsing tls.key: %w", err)
+	}
+
+	return local.NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy)
+}
+
+// hspkiCreds returns a HSPKI certificate/key for an SSO user. The returned
+// certificate is valida for both server and client usage.
+func (p *prodvider) hspkiCreds(username string) (*pb.HSPKIKeys, error) {
+	principal := fmt.Sprintf("%s.sso.hswaw.net", username)
+
+	s, err := p.hspkiSigner()
+	if err != nil {
+		return nil, fmt.Errorf("hspkiSigner: %w", err)
+	}
+
+	signerCert, _ := s.Certificate("", "")
+	req := &csr.CertificateRequest{
+		CN: principal,
+		KeyRequest: &csr.BasicKeyRequest{
+			A: "rsa",
+			S: 4096,
+		},
+		Names: []csr.Name{
+			{
+				O:  "prodvider",
+				OU: fmt.Sprintf("Prodvider HSPKI Cert for %s", username),
+			},
+		},
+	}
+
+	g := &csr.Generator{
+		Validator: func(req *csr.CertificateRequest) error { return nil },
+	}
+
+	csrPEM, keyPEM, err := g.ProcessRequest(req)
+	if err != nil {
+		return nil, fmt.Errorf("when making CSR: %w", err)
+	}
+
+	signReq := signer.SignRequest{
+		Hosts:    []string{},
+		Request:  string(csrPEM),
+		Profile:  "client-server",
+		NotAfter: time.Now().Add(9 * time.Hour),
+	}
+
+	certPEM, err := s.Sign(signReq)
+	if err != nil {
+		return nil, fmt.Errorf("when issuing certificate: %w", err)
+	}
+
+	caPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: signerCert.Raw})
+
+	return &pb.HSPKIKeys{
+		Ca:        caPEM,
+		Cert:      certPEM,
+		Key:       keyPEM,
+		Principal: principal,
+	}, nil
+}
diff --git a/cluster/prodvider/proto/BUILD.bazel b/cluster/prodvider/proto/BUILD.bazel
index 2efd457..0817dfb 100644
--- a/cluster/prodvider/proto/BUILD.bazel
+++ b/cluster/prodvider/proto/BUILD.bazel
@@ -1,3 +1,4 @@
+load("@rules_proto//proto:defs.bzl", "proto_library")
 load("@io_bazel_rules_go//go:def.bzl", "go_library")
 load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
 
diff --git a/cluster/prodvider/proto/prodvider.proto b/cluster/prodvider/proto/prodvider.proto
index 1ae2798..ba5bf9d 100644
--- a/cluster/prodvider/proto/prodvider.proto
+++ b/cluster/prodvider/proto/prodvider.proto
@@ -15,6 +15,7 @@
     }
     Result result = 1;
     KubernetesKeys kubernetes_keys = 2;
+    HSPKIKeys hspki_keys = 3;
 }
 
 message KubernetesKeys {
@@ -24,6 +25,13 @@
     bytes key = 4;
 }
 
+message HSPKIKeys {
+    bytes ca = 1;
+    bytes cert = 2;
+    bytes key = 3;
+    string principal = 4;
+}
+
 service Prodvider {
     rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse);
 }
diff --git a/cluster/prodvider/service.go b/cluster/prodvider/service.go
index 0409884..17dfe6e 100644
--- a/cluster/prodvider/service.go
+++ b/cluster/prodvider/service.go
@@ -69,14 +69,22 @@
 		return nil, status.Error(codes.Unavailable, "could not set up objects in Kubernetes")
 	}
 
-	keys, err := p.kubernetesCreds(username)
+	kubernetesKeys, err := p.kubernetesCreds(username)
 	if err != nil {
 		glog.Errorf("kubernetesCreds(%q): %v", username, err)
 		return nil, status.Error(codes.Unavailable, "could not generate k8s keys")
 	}
+
+	hspkiKeys, err := p.hspkiCreds(username)
+	if err != nil {
+		glog.Errorf("hspkiCreds(%q): %v", username, err)
+		return nil, status.Error(codes.Unavailable, "could not generate hspki keys")
+	}
+
 	return &pb.AuthenticateResponse{
 		Result:         pb.AuthenticateResponse_RESULT_AUTHENTICATED,
-		KubernetesKeys: keys,
+		KubernetesKeys: kubernetesKeys,
+		HspkiKeys:      hspkiKeys,
 	}, nil
 }
 
diff --git a/cluster/secrets/cipher/ca-etcd.key b/cluster/secrets/cipher/ca-etcd.key
index f0e9f20..7340d92 100644
--- a/cluster/secrets/cipher/ca-etcd.key
+++ b/cluster/secrets/cipher/ca-etcd.key
@@ -1,66 +1,66 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=E3lv
+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
+=CpOb
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/ca-etcdpeer.key b/cluster/secrets/cipher/ca-etcdpeer.key
index 5afc294..0be32a7 100644
--- a/cluster/secrets/cipher/ca-etcdpeer.key
+++ b/cluster/secrets/cipher/ca-etcdpeer.key
@@ -1,66 +1,66 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=UICz
+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
+=WyOC
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/ca-kube-prodvider.key b/cluster/secrets/cipher/ca-kube-prodvider.key
index bc0eb71..976c6e8 100644
--- a/cluster/secrets/cipher/ca-kube-prodvider.key
+++ b/cluster/secrets/cipher/ca-kube-prodvider.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-Z93hDF6HQaWlS/dhm7HaUbvEXmwd38dTq+sQ
-=GZCJ
+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
+=C8lU
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/ca-kube.key b/cluster/secrets/cipher/ca-kube.key
index 99362b9..ba1c608 100644
--- a/cluster/secrets/cipher/ca-kube.key
+++ b/cluster/secrets/cipher/ca-kube.key
@@ -1,66 +1,66 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=A6fA
+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
+=rrIB
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/ca-kubefront.key b/cluster/secrets/cipher/ca-kubefront.key
index 78b1a0e..97a8e46 100644
--- a/cluster/secrets/cipher/ca-kubefront.key
+++ b/cluster/secrets/cipher/ca-kubefront.key
@@ -1,66 +1,66 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=8qiu
+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
+=oakZ
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-bc01n01.hswaw.net.key b/cluster/secrets/cipher/etcd-bc01n01.hswaw.net.key
index f6aeba5..a36a6a7 100644
--- a/cluster/secrets/cipher/etcd-bc01n01.hswaw.net.key
+++ b/cluster/secrets/cipher/etcd-bc01n01.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=F2tt
+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
+=ChTK
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-bc01n02.hswaw.net.key b/cluster/secrets/cipher/etcd-bc01n02.hswaw.net.key
index 0733143..ecb95ee 100644
--- a/cluster/secrets/cipher/etcd-bc01n02.hswaw.net.key
+++ b/cluster/secrets/cipher/etcd-bc01n02.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=aQG0
+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
+=y83s
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-bc01n03.hswaw.net.key b/cluster/secrets/cipher/etcd-bc01n03.hswaw.net.key
index 0bc7bab..84be5a5 100644
--- a/cluster/secrets/cipher/etcd-bc01n03.hswaw.net.key
+++ b/cluster/secrets/cipher/etcd-bc01n03.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-hQEMAzhuiT4RC8VbAQgAoCO/5ak5cizr4YUloSCvfmn8GG6gVqVVjwx15PmUQDm7
-DSleXA7c9pfZz/xqJatwUxgwaqhS7dN05Gt7cWbb34B5KJ9tdlwFtot/iGHT/Mrd
-rB6sk+cTSnsvmPBoFankJ3KTEnxywebS9w1wWvE0N9PV0uzyic7CQP/c/kq88UMf
-Y52GbREI6nfVhJ77YVKiVny6944FspHWLKO/Uei3ea98N92apYNtjo23LI7z0Ccx
-OwW0ypmrG4JW4oz/LoPiM/lwjmwaXQ+CWZ+WAIk3LUPoSA1TZwRSVWibxVKOmtEk
-9lBj7L7m/1tHfsSWJdFAd2on9PXhAgnkPJ9XD9jw+YUBDANcG2tp6fXqvgEH/iKl
-lTBGweaeD+C4KP3k/+OQdt/6SGY4cAkqGBS9L7mdKzfFCJ5gPQ66OCXTwWluJl8e
-GQXrcqB5z/o0VwNJ42QNMe3FrFtCOwOIvaSeyfMoPQXXxqUi7HJhcIzkBAA8yKKQ
-NUgo0sCiHeawH+rd5ELWuac+jyucESEfFAj58ej/MB5bz4+44AIUyBdIWOex4PBD
-E+NctUuDQaJo0SAr8chyix2tQZs3if4mVkmw8mjOeSag81hM/lqkzbWG11DDTJVe
-pcFKVSpfzYgjrRFFM0Syb7O4zewYB0qfuNknqOasjY1btSFzYuh9PupEMxTbwA5C
-vPdLuQUxy4yJjaruoQeFAgwDodoT8VqRl4UBD/4t72cUlQBI7Fz5YPEN9608Z7BM
-icUS9iYo1VX9hEV0rqdV7MogSiHTF9e2qdHAR2xcCjPZmiWdxVN29vQNqxv2sKqA
-hGJTmjhEvaEPL69mMZoENhsU1MzbEfONPcROv2xkCb212Gs3HSckr/Z4xYwyJb36
-o3C4xiTfXWpE39brqEMjI7AqYn74yCTNNgKQ/10CPuvQvcgs2ely+jksKU4lwOIK
-DRhZ7VAkjzg5NsPYC5lNZ8gu5CnXSdoVY5x7GQP9Z8caCWidWbgl/AouiSMR0g8L
-0Hh8jiFTwm/aln6Y/1HNDfvR6fhAPz8PfWH7W7ikFvX4dbilh0/sMTWWpG/CwDoh
-aty2DQE1O/ODNGp5aDX+t9KXThpHroq9IEp2kVs4KKy7+ZtNRVQbMITEAiuXI32F
-i5kdY93PdE/yWr36XwAzzPigTJ1qnQWP+ALYWPwsY8QuoridA/010eqnzCeBZ5fq
-F6S6OlBraXcv4ajJHYqpV944gGRMTmFKDnmcGtI/iNgxaH/rtyXqE+ikauL+5oRS
-ZW4UhBqHsmk1r7eCF0etkdFXkogcacc4zEHA6G4t3MX6qBLJHIRdxhIupA/kD+Zz
-Unatg6EHs3joMlKcygXJAoEtWA3udJnJ6xebzkGcmAhesufTkirj2UEwG6Roas0j
-xdXDvbCx/Li3G7WVe4UCDAPiA8lOXOuz7wEQAJzyP+csc6gT7T86QWKY2kQRX+Dc
-VZGjKR8egx3byf9Dwaab1dVQawz8ac4t+UFC7CSruAePUbkmOFpPb6sOVisXEzpI
-BscVgpSQ3+G8VOSEPmBfeEieS977MQFwNhhJgHHhqkZIgQdcknTDoEVx+eJ2w7oh
-LQ6TdKTn5VA39hNdDxFAla1sXos2dHSfqHRXX45eIbTTJzVT9rHU59/4RnsIMj0+
-CdgPwIRxqtKG9ZKSB7qCar7STMxhf4+Jz2f4Snsd/QLBvFzUjP2tptprmHoSLVhl
-kl9xyT1AUcWbpEKhpXc+mzTuYSqj86vbmHLE+S2PhmlN5pECdouvkRLHtm3EfrTM
-vhDmfU4sw5U7G5zupC23jwLIvmuST5/EeMrUVuKzRAKvRPJ42nzoZAM2G0ce/DS5
-1HQmwML5SvpauXZOfrGGB6dbfZPLbbksgH/IUplk/hpJ2i/WR9aTPvgvlN5hKprl
-AEq0rfqyob0iX9bPXNrkoP0XAoHcPGMtyHCuNQB3V161/BsTxaDGBzx9Y3yN51ZP
-kBbkS63GZuOG6lwrCCYeT0JtLft/+xMN05lSw4ciEQ+t9WcNMADjJptTkBJAWVbE
-e1IyfIP9AaUZhULk4K7Gi68lBlTDItZI1MVHXrzaaxdR2tK+D7AD4hsJYZDeIxi3
-G5zXeXvR64NdNi1u0usBHHvg02J1DZIljD3p4c0H/aopwFb5/hPa74NaGmj5B/BI
-ZkYKO+SssyMo/ODdw4xcezDRusx1ZVNR5kP/wQuoDxR67GJoSQ0QiEOK76/trXIp
-SX8K50aWOaXqQ8cCP2zomTp4PsCc9v7AEcjxDoPKDrZBCkqcrMPNwfbXEUUTTOMZ
-Bv1ivSOKZLGuk52WT9mc4soR4ID5KSxrkrnVzJsdo8NdgKtEyFCwq0c82iWeqIYD
-8BzRSFlE4zIrcXlSERtbe1makaiptOZnXg7hdu3WnzHfY9qinecIHxFDctPqzgA0
-zeDcPClDV0OAyXz9Gz/JgAtjoq8Efp3DcoEGCTCe0okk54AN9f0NNnpRWWYbS6Tz
-GbFS0KpIdUbpvMTuEo6A3GFDvIOcY9RG690DYblqDgr8oyYIQU5VgMC/VG/bMFVs
-5O+lAHlUGGhzi6zzLHNCDrhaZIk9ic9YVsx7Gi2V3JruHEvPOMEkPoJMC0WYDv3x
-bx0kqXzOPvzQ9Jn6x6x33OyA/ymD5pgNoRzO2FX0GPrTwrgcAZaFkqIjijHCp69D
-sCRg2O3V8Jj44XyZTgFr9pY1y/yNrqWkeJ1PJRUYClZSgbrsI8k0bYz55ijnWV19
-oPWKdDn060kbusmTQE4MwSt4oZpU4OTFzPy8vugdkPZ/QK9qewXJUdOd1BycxRZq
-4TXaG0UHXE0zVNQGRjrgS1AZ+FqR0GDdVRTgHvSOv9ysy1mWVj9NEDHaZqSHvZmt
-QJqJKcZ6S+3B3vwtOIYroG6EF+QZuwWNqtixl63gu76RF/Fc1bNFkcB1E51MwmY1
-XbrPMl0SpRxTOe49Ha1QhKfwfldFI+Y8ig+rsmoWgiAc+7sAH2rwZzpuEeg90phU
-37YM4mVrid6u+v48Ux0yBT1coyXDNKdlqrr6YWjdDVpyI3j2zB59g9u5xwuZmXSO
-rU1W9MNgfq3BByp5zXwTy8Qle7JD1kzSHni51PGomjQ/XBPt2pjxQ/3l+/c1lWHt
-3ahq1dxmuh+b72IELQ31afWCQoYQP3tte51GZgbQS0gX1yXIk3ici75xHkoO1khP
-3SWemNY+O5miPt/drIe/33lh7hzJ649NGXF8jFSCQSNeJ4IHkggbxQl9at73bLh0
-hDP7Q21hvk1NHYoVZuutUHw3rJT80lGY+4fSLvTeWrOSBDKyfN5GkHb3EvE+qZkn
-0IzfmIBeeFoWr68P7LsQ+n4z2Wf1TsFjFMKL5Z3+DBaGp0zdQldTU+7bbZPKVqdT
-niVr14ty+TM9g2qdT7PqxjlBC5uH7GlRbl02LPQ96U8doUKLepgBg0Fxg6jUt0LN
-EDQpaE0zsQTdzfQVFgRgXo1qUKcQ7Uk2P0f7bX04f0nG/qG2+b7qSyTnItu/aLHP
-Jn1F6oFhPoAa2Asa0/exwvO6hKvQQ9BJZN41l/qjdTZjHQcmIYd8estwHhoY7eE6
-ZF2h3c2pG34TNmL2P3fGwTBsMHL5rMUn+oxm4/qEpmAG4E6gPkgvUaLvuHQQYt5v
-59X3JBhlP3i8LKSw8dOKDjMXs1HktVB4nJfN2fquOgES9aBlAnI+LSdCZ/qwESCX
-jhHtlDbM4JpsAe8vwOrgzpjtRpkGoXICqB6zpxQStx/Xugl+zvn41fsP+KiUTMwb
-UF2/ew8O8C/gzqD1JNIzmjQ1aDso6ZJ02pHTfQrQisFkCRLT0HXJlvrlTueb2Zod
-EyrSm1v8Otdgx6zn1wyu03uhHpFPNRUpZAqmUdwVijjt3ruPgs4orfrl+EW+yAt/
-bEcRViwlU2/qxauBzEs6ghEX2P5BGusMWu8aHq+GJgIya4awAth5+fTAGESuDcR6
-S41eZZUBNXzMR+KKN5VJ+uzlMU1Rcv+qVSJYksZkaP1i4gFJxAf9FIrBwaaDSrV/
-0DBb2j/lfrhXyzPJsezKNagvMdXe4N5xrb1Jg1ihss1xdsdKwpnzQsQ/6wz34Fy5
-Dey+WdWqSY62qujmt7wbdESayXsinUgu1cH+CxKbMAPgbVsYwqIm2QWtERWN2vEp
-DSi6ngyiZtv9ypg4EwcTu5j0xFKI/51iWEUB7vFGk/bIhJOdNxgeVCV3V8kn2Xx8
-k83ZHvXO6puW6RVdxrHfG5rWHwS8FpH13TYDtqwJ3Vn/Bmac30j9n+kfYuxL9vll
-X1rpikK4vXkAT3O44DOtxMy6VF5wp1izhVQ8vhifs3qN6873+Z/+LQW3CUZgQjJE
-/9HxiSnmkgEDC4oeLAlkjLGGb+pmwNBsMS0SsWQPevo9hy++DQdmbFR9KHWyzCjc
-ckS8Rse/MMXIsLfjP2+Mb4MBxCY9TRVnGylHGobblV2WNzOIeB8ciKBEam7xtcmp
-XGoP5khImW/G6loQSrEMB9zwUGK44TR3MeacFmdR4PehuVbX8BUDomUHmvi9WLqh
-wgalvzlF+JXxZMUwoDDMEDDanp19KeJ2SMF1dswONxOllquRrG+rLLO5JWpe2mda
-2SQXelCwhzdKZhJCisBaI4CYp2qy8JyDMUTzcghzgLycnDfPR+vr2IOycbrADNk+
-2DpoJIHxDas736YUKz0z3ogvIEZPLaPUn0xRahtqwzcXMvb3GR3GVRVppPY+wI1h
-1muayjzW6tV/4aM12youg9DpWoQ8bwqdF5fOmCEjqwnQUjRh8lu0SmN+BN7bGhhN
-lJmEjhL7msSEgolNs1V/5TUETfQzLfe30Bhu9Hu9gb6Whoerl/fIb3oSkbCfusE/
-jDXk5jZAAyQHfAyVox89ebt1tLYOi1Ey1Gvy76UhJqD5m4coE5KNn5hTToDfYq6p
-FBn9QMdPXUMO4x4UFdMvwKkWx8NaTnNRk1oMXl3XFWbL10IA1UbJmCq6sA2Qcgx7
-nCT9HLGu03AnYqJlzXxAHAf3h+Pijd9cZ2v8ckdQmYEv5zXXjZCRWyIC2XdRM5x3
-OQaktuiKcwB+Cw6mIovpAlxsr6XbJHYwK92VsNWWC8fadfpKpgowNzFMN0xbucIV
-PyEfVwsO/sQpA3Ardr+ev6mWwNJElCeRwNJUTw82r4rvBuRhONkSPp+nStBrwQFM
-UIwBSgntGE4077HbldjY1Asf0cTcReSEPD89/466U5RKHNMp03OQmOPSZ8h5Uq+P
-KyQBSg2shO+DwB9siKWHsyLm976zwcFlLxKEf3HhOdnIaKZzc72Etv34/Gki8yHZ
-XQ35uRvQNjkqN6LQELtNYoc0y3BxsUODxem+/funO/eJNrtRgeXYWszntJZ4H/gs
-3EQ+Ha/Img+yyUrMzu4CneicW1GjJcXiLEzVDL8gh3TJywgB75UQ/810tdKDXjYI
-Bw5wr3Ze+/G/ldbUcw8f87RWY7iL1TFA/DweXb9ZjqQiSBx7AQZShDe2aFZqzI2Q
-HkBOBf0cj+UoXnKDRSf2vaAIvwZUUNuI0MGxa+6R5Q==
-=nE1W
+hQEMAzhuiT4RC8VbAQgAs6h2bfoEur3jT/8qjRQElHVDhPyvQY6ZQ1ffx66ot73e
+72Ks8DmfV5atk2p07h5Eg6EhGU6jXL60gRFCTGt/Oax3AgiYN4M6SVP7Q1jB7HVK
+KYxak+ILoBzv9SttNw2ChHENkqPpke9RZVcnCqLlAPLeNSd8jHX8NhSXaFAy+J2W
+eTpZfpxNJHe9DNMPn/I/vGNypDSUcQZENn1hZJX4hruqQSK0hO8NqCyNK0/EmoiS
+Z9mG6B+MKA/yDsvbm+zc/PIbW/iY25iz0mg9eScSCoK2pUbBaPMIFfSM7ol7Iw7g
+ETIVQ2wAirI8aEVojJqLFE/BUpBLBuCzKcKzmZO73IUBDANcG2tp6fXqvgEH/0xD
+0yy/Hyb4UQdyafLnuNz7QVwwclwC28JTKU0mBpGh+BS3WyeIhfAKGm0ZyMu+0UwA
+60gaCh0KlIhJJMBEb8M2MhAIPsicu8AkBblIf9oB5mn+r0Ha8rUAjO0EG2j0Hj69
+oyI4/sWhNtdJzA8jr/4bZINclhvnkpeb7bn6Y/Zop7EA9caxv1P9xtn1ySlNdYTA
+6DFawS1P2v3tf69hy9Eg7E00GyH+iyLXudGXySzlg+38zZYVUJAWEkOeFgOaN4bz
+T2keYmU0cQhCWToT1kKg5DPJyJJ1t5wzhhRN3mCQEL6PCb006s22427ZDHNplZx2
+Q/mmj01tNlEUcBKtkIaFAgwDodoT8VqRl4UBEACJjH94wIsEX6o3kN5l+ECjDdGh
+zadqadNuXOJaX7g3DcLeU7Zt8g7u7E6Fi7HUvHnS0+G+NBlC2c4PeKx796cufVhJ
++dr0RVAidfFcRb0z3tZayhS4wFeNYmbgFrFyb1UoacjaSH3XFQL9SxyxejQ5z0EI
+xHEF4SoObWxzaUAh0aWu3nMjGf9xRaKiphUac+tdUlXb52yG76jPGnunQpEpdB2N
+P3KAUhWCs16TC6kD3WJ4NGqdx0eBym1gRTKFNwOp7rKPEfBS3rTeCtdbv4x29fWk
+09zsnqh9xcMBcGSiOPRkRqsVDsja4Cf/3cWCR4lSsYWok73vihUk9DsWtwzPxJ8V
+zPW2H7LP2RaTksY4Rop50il4jdkzRMD9TODiELGkLiP3cDtqstYzIqwlxJZlUUQJ
+qiVbnDf9mWU0I+tG98elBpnB4K2wGt8D7zai5kJh8P2v4KYHBUoibnSIRlpntQrD
+m3muEhGW+kaoNiVwu2LhDwJ/mkKJ0QYG4LpEli54AClRfiKX7cnnPHS46FeaIGUH
+9llzhOnzDwhNUoQuQF4xloyNYydvdImeJQcMEnFP0FRfkJ/SGNMYHXsAk2MGi7iV
+LPpBWKyoH+sNQtsdGWrY5BI7m3B1Y7BNasEV3EkMXdcNW01wV494vwXD5n7ADcJn
+RedtIF7tRt7DVO0MkoUCDAPiA8lOXOuz7wEQAIMYOgO1tvYQ8kJe7iFVjcTPaiPu
+z90xAt421Ta4oMeuo2e6sPW2HtAeDx7F21k4lTP2jEEkDV2o327aaEi0Moz3Xekp
+uM76O7MO9xHt9YvE1Oathqh6aYiEuicXjBqKA3J2fGVlnGVKzwClnwrYLFEdA0QG
+S9lNSKPSwY68brUZK/Ny5lDb5k/dfGDHPwXXUGUfxq5eBi0+LQeN3wP4WGzvjaci
+iFRgX37rmBhEJlx5jBoiIoO8pVOzzeTxVoH68caBQFaRA2YXK2cmh6lLVpMRVcIm
+Md63xIToJvHv90HphlI7+7j1fxqPwJEbfrgn4DKMMbwHiQtZhO88HtIKqdvZJAIs
+1P+r7g9vQ/cUH8IHxqJT33JnMvui5kpc3Bgku2Det26o8mNOiMflLoiqZysxcwOg
+C1MyPBzYAqQ8osypMProkoAVWv5P6v4uBQFbNlzqvPxyGyhgmTNMbhZDj8xvBP2G
+mvplX9F2OCtJXM/DAhdqNyQ3kJ+Is+ZThER1V8ovKshWLjXK9BRlT5357+3S5cck
+CUw2mZC8y/6hQTMK4Cqw2kdKOnD5k1RI1+xwG2S4gdBc2GgYsEQGcWd+lFdkvziV
+mrWWuObA5NHlZ0ZSOGIBc9hFTgVTkbJQ8VvSM6DgY1m+SHHj3w06DWaaBTFyuoPK
+r/nZcopqpEx9jIB40usBXLU/0B27dlx2P2bHbgfdMXeJXGgAFLiT8R+AOhAxX2K7
+dhJPERO4kJIqrqU/fa7oFF4T8VTzr9+x1bnLllRJExXT6+QKZRS08UhU2ZrwG036
+UU9hiD5azUMc1FAUtWe5hHCYphcgJ+6QcrZuiRQirTcSvWCe5uIzveWh+m+YvLbT
+hUCBwtz9n5JlKyUm9bOTnwydkQYb2rnh+MIPPohJsOV+UCwQflKD2dAs4ySI7g8Y
+tEOEj3FBFHiZp7dMmLkNVfdu2ZZWHpA9ne8x/XO5mK8P7k5s22xuyP1dsEi6CMfm
+mnshnQJ1O6CWURu5+5Dd/zXicpXTnnzI2YzFoy7erVdzj/pRkkR3HrVJbqUF/c+M
+kuWWGi5V/y3ZA6Esaueh8BEvyUBK7BpT+vu2cj+EJhGu5ppkw+IAJf1S4X9tW5Zy
+LnPHFT++71ZvcKQR+Y3jLjZ2zcYsXAqQ6b2Kb91tNGOZlXcC7OAqjrfn3bUzjeY0
+bjZoFis8RNgdxouCaHM/nCYfMgjixYHi6HeccSrn8TatLWuVw3fsDI7sPwAH1/Tf
+IEnKKKHXeeDEGZ/8GwpLZORN+oBculaEaDBiutVReqylFu7jGaFS8+oiQYG4tWj+
+OvZIcKKQMHhYDmf52cziNUhMbZ2rLd3zYGts8T1yDSwvGla4nrOsE/+azxE7EdDd
+jZyPiUQfe7McB6C7P5exFg6bJ0IVsiiNFGNH/GkMxqM35K01VDdCZAcuQiiH6ar5
+SwPLnE2vlD5A/jSR84nH3/yZdfRgphy/CWDmm2j/1J3aQ3AcQ7eNL71yu+RngRtJ
+XiL3+qSYxbAB+rOfx8m0GktHfwTt+QATJEEEMjQPa5bjrYWynE7/RBA9Y3gfh2Kg
+fuwbgTJazlMltsrmMfo0i2sDfQC2csMybQ/S2EQSru2Jj+1tOXAKxxeo3tYntWxv
+MLIKiNj9t5rtnJOgEeHZi8TVCKau4bMrEK9eaoQ/vyXiZm28JW0zxK+xmBmMo12f
+9xwzzh2CwA1HAU03dge3TBblUvrda/hJ6SdOiRJXYY5Dd/ZWmDp1+kT29k692wEf
+7+Gshv+9bWLCsMbvlBLkZhvcUGOY6bYGd3RRJtGCWQi/vOIl8L8jrYGYruKCpsAh
+jnrYi3XYtZe4nehYMWKmXiggNsyYkfjkrleKAqFLmLjWrXLhzDTq5HCMbJS195JZ
+63SKRGLqpbG601iN2FDH3Y8xeBdtPqYOENRV2LAAQX4/JHxMSdua6TEjeBwwtW25
++Qz8D0InOk23MwehbHVLmz/BUi4524y2ncunPa97uSoO5dxC0gpq6520j3qPMb16
+Cc8Skm4n1rZmYW5rq7pSf3uMQ1yjaGD1xm783q0rg8ty7Yz2MiKlOxGGng3UqN/g
+k3N/mh7nsB7qXupLDn72MLa02mBLEn1x47apUPr1QPxTcpOKmp9WEudohVoT/DSd
+folqZlHK4j6PP+BQMCVgvLlHHzOldTKW4+1hNS4e2M0Sm5WpnARA+6fntlA+G3Dr
+ShvSX+SwdkCjStspO9eSzZsM6eEESaizKi6RH1exS2LN1rTsBRo90p1MCyTRCe0D
+V75jxyaYzymJrXbXj8vOUB6ZQE6/Iw2lIl7AFeki5mnZ6fCq6SO99Sf1BF1i1q98
+r75qTW/MZlCxHRP/kcii5Dkx6FZqVkYNp8+njYzEFtI0tDoFZ05No4cbq2FNOf5Y
+VQ+f1dlwhrQnrs1IigGmbGlVYIxaa+S3URd5P63hUSwTioW6A70ytaWJ1Aa8xSe3
+YQ10WOkU3McQXkWv0c/Kp4jNo/t8hKN9d2RfH2InWqhtLUhIFNp8G1ge8ujSd7y7
+2JN6FTNRTOatqCX1SXf/ygI+efowd8xU2btpORxP6CrYU2nJgEBeSWiS8jr9aZXB
+zE4HySjjA3ggrTonUOtcwzMImWWBFR7MFRSYSAVSqsOhiIrSR+fpCCfaKy/q+Muu
+dvd2S5Zy0wlc68rgyF3RIrfI791u6Gc6rLwkCeeBnWSb3tQJome60n8RcwOTJqB+
+6rtNmo5m8AAG9MlxvHYI80FuxiR3QbwxF4fQYKK5dHIqJCdmXXbhynbJ+kMtonEj
+2M3CtYaeMMkCZtG6OXZgd/uToXuwgwSVthP3DZpQGCOqeDC+57SVRiRCn7EHhL11
+O212WKTFKr7yHNK7mPsfYTFRgkF7ZtkG98JqBOXr6oOIEUqmUL04eQTWIRePssUy
+ZPFm94itmfBw1o2CVp19gMOSsbcZ+8otPvcEt5ttjm82VbhjZ4Pid0+tNwuFMASX
+/r8RzhF9ZDZC7OTiVgfA1XLtf2ddVFqQKC3OMKtS2Q8sL8nliHIlDXqta06NKegc
+nYpCb70W4NV0fiRe2kVncCqVGSMdznt0kDZfEuwLQwvq2wd9XjLMqsM6bqmmbWaa
+jMaFbrFaocm7DFZtKNk0AQUQlfCCaRgP5udirnSjxG/vvFGcsR5CzcfrKmyAew80
+hg+d1P/RmHQo/JesbrKBBYPLmi9znJmT7CbmmS70bghvxQ21Yn8dLHeY5r9daRYz
+K9Yq5RGCjZJhrr+3eL//IHeNC46s4v6+K674ONDUKA9zReex73iw/RqGP/sOiZUo
+Gx0IygJggKpLI5f4buD/x/ll6klfzuZSXYf90RQP/la+fkt3S1EWL0I8J19U/8X4
+0GcB0fxiTwHoKU3DF5pD/tEZjh/GDeS8PuwL6hU4EN7ULu8/X7I/59YNLgjj3emC
+i7q9A3h4ov9WokBt34+Wqmdks0htcxZnimxCYPjbp7a1aC64gQ0MVu38sQSqbwr6
+BTsWyapQiskbAF3gZPrq6vJW8f1DuVHgqnZb4NSrHLF50y7xjkg4+Cx+EiXocyey
+3ccT9OXHI+5646S+4fW+nJOUBwytG9Fj3i6I/EQ/jzBi1bmuGcBxAj5xG/3+91UH
+xv1hUyNTDGVwtjkCR8S+04b0smGxPPRm8ccUgDlnvYdhsXqJYbAnp7QOQsP9oIOr
+HI+D+h48yiNCgx3AkvGU7j1L8B5T90sgClqcR0Zayh0u0N9gtEjeBxSn05J5A4+r
+ilkohoQ6CFEtzF+2a8nssHZQW/2/mojfD9NmkOHuAQJJxDWhTQhb5JTSWTu0piy6
+WVtjB2oZnVYTvqG5IUrwU15r53OOCbakaMZH7fVfrAqQmaZm7OFr1a2/ugkmKm3L
+yNv1IbPDJAMQwzvTAuRYx3mz82fEFvaos6vJgC25XOXRw4XLF5x7JaQ8SNVSCIRE
+khEGQdBHA3ee6D1aHSNiBfjl+o/NbVNImPP5Ys33NnxgEEfMPTkOvK6km6+pbe1S
+JjO2PvBpJnJuEbps18HqvJ6H++sbFcecsjbKXPiv2do4bO4fLDE+qPV7Ds+lt48P
+rwXsCHFJ22Wfq/qoMOvGjk9ZHa5O7lzNFMfw/H/EtgOK0V0=
+=sxdX
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-calico.key b/cluster/secrets/cipher/etcd-calico.key
index cf221bc..105cc00 100644
--- a/cluster/secrets/cipher/etcd-calico.key
+++ b/cluster/secrets/cipher/etcd-calico.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-cFsltn/qbVmbKLR9ZQ2RklaYBIijUbgvxlX1Tw==
-=4YkA
+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
+=587z
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-dcr01s22.hswaw.net.key b/cluster/secrets/cipher/etcd-dcr01s22.hswaw.net.key
index eb87b8c..ac8ef15 100644
--- a/cluster/secrets/cipher/etcd-dcr01s22.hswaw.net.key
+++ b/cluster/secrets/cipher/etcd-dcr01s22.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=3Zkg
+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
+=BAut
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-dcr01s24.hswaw.net.key b/cluster/secrets/cipher/etcd-dcr01s24.hswaw.net.key
index fa35f7f..be05ccd 100644
--- a/cluster/secrets/cipher/etcd-dcr01s24.hswaw.net.key
+++ b/cluster/secrets/cipher/etcd-dcr01s24.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=pd2W
+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
+=D6aI
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-kube.key b/cluster/secrets/cipher/etcd-kube.key
index e9f8156..5079ad7 100644
--- a/cluster/secrets/cipher/etcd-kube.key
+++ b/cluster/secrets/cipher/etcd-kube.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-p1I6KMSwlNOyHzW5qrCNT0RhxEnlAXZMyds=
-=ngQI
+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
+=RMD2
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcd-root.key b/cluster/secrets/cipher/etcd-root.key
index 5045ca9..d320418 100644
--- a/cluster/secrets/cipher/etcd-root.key
+++ b/cluster/secrets/cipher/etcd-root.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-vasMDIaJQas6iacQE4+JVQA=
-=HJA7
+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
+LIJ/tgIJER6+TtjS0hOyY9eY/zA+
+=TsBs
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcdpeer-bc01n01.hswaw.net.key b/cluster/secrets/cipher/etcdpeer-bc01n01.hswaw.net.key
index 526c0f0..f1dd9df 100644
--- a/cluster/secrets/cipher/etcdpeer-bc01n01.hswaw.net.key
+++ b/cluster/secrets/cipher/etcdpeer-bc01n01.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=EBRn
+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
+=flIF
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcdpeer-bc01n02.hswaw.net.key b/cluster/secrets/cipher/etcdpeer-bc01n02.hswaw.net.key
index 7d16e6b..27b3d5d 100644
--- a/cluster/secrets/cipher/etcdpeer-bc01n02.hswaw.net.key
+++ b/cluster/secrets/cipher/etcdpeer-bc01n02.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=z/eR
+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
+=uJtW
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcdpeer-bc01n03.hswaw.net.key b/cluster/secrets/cipher/etcdpeer-bc01n03.hswaw.net.key
index 6c1fc74..fb0ef5b 100644
--- a/cluster/secrets/cipher/etcdpeer-bc01n03.hswaw.net.key
+++ b/cluster/secrets/cipher/etcdpeer-bc01n03.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=97dn
+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
+=KgJ/
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcdpeer-dcr01s22.hswaw.net.key b/cluster/secrets/cipher/etcdpeer-dcr01s22.hswaw.net.key
index 7f2f5f6..c55a881 100644
--- a/cluster/secrets/cipher/etcdpeer-dcr01s22.hswaw.net.key
+++ b/cluster/secrets/cipher/etcdpeer-dcr01s22.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=zHKz
+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
+=kTw8
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/etcdpeer-dcr01s24.hswaw.net.key b/cluster/secrets/cipher/etcdpeer-dcr01s24.hswaw.net.key
index 04f9df0..80e8922 100644
--- a/cluster/secrets/cipher/etcdpeer-dcr01s24.hswaw.net.key
+++ b/cluster/secrets/cipher/etcdpeer-dcr01s24.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=++x6
+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
+=w3D0
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/k0-benji-encryption-password b/cluster/secrets/cipher/k0-benji-encryption-password
index 2641222..2769ce4 100644
--- a/cluster/secrets/cipher/k0-benji-encryption-password
+++ b/cluster/secrets/cipher/k0-benji-encryption-password
@@ -1,42 +1,42 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-F4asCNxWg4rFkBbe
-=DFc7
+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
+w4gtXHAkzOblBLP32p0nJQ==
+=zIwf
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/k0-benji-secret-access-key b/cluster/secrets/cipher/k0-benji-secret-access-key
index 5eab650..4219ce7 100644
--- a/cluster/secrets/cipher/k0-benji-secret-access-key
+++ b/cluster/secrets/cipher/k0-benji-secret-access-key
@@ -1,40 +1,40 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=dBs2
+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
+=E4YJ
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-apiserver.key b/cluster/secrets/cipher/kube-apiserver.key
index 840c60a..f3b56da 100644
--- a/cluster/secrets/cipher/kube-apiserver.key
+++ b/cluster/secrets/cipher/kube-apiserver.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=BvBL
+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
+=U+D5
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-controllermanager.key b/cluster/secrets/cipher/kube-controllermanager.key
index 5d19955..bd8d879 100644
--- a/cluster/secrets/cipher/kube-controllermanager.key
+++ b/cluster/secrets/cipher/kube-controllermanager.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=Qz99
+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
+=i46T
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-kubelet-bc01n01.hswaw.net.key b/cluster/secrets/cipher/kube-kubelet-bc01n01.hswaw.net.key
index 648dcee..803a731 100644
--- a/cluster/secrets/cipher/kube-kubelet-bc01n01.hswaw.net.key
+++ b/cluster/secrets/cipher/kube-kubelet-bc01n01.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=P/Hv
+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
+=04wM
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-kubelet-bc01n02.hswaw.net.key b/cluster/secrets/cipher/kube-kubelet-bc01n02.hswaw.net.key
index 1393705..9d6d624 100644
--- a/cluster/secrets/cipher/kube-kubelet-bc01n02.hswaw.net.key
+++ b/cluster/secrets/cipher/kube-kubelet-bc01n02.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=+UvF
+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
+=u5Jn
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-kubelet-bc01n03.hswaw.net.key b/cluster/secrets/cipher/kube-kubelet-bc01n03.hswaw.net.key
index e7e0f0a..936ecc4 100644
--- a/cluster/secrets/cipher/kube-kubelet-bc01n03.hswaw.net.key
+++ b/cluster/secrets/cipher/kube-kubelet-bc01n03.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=WYMz
+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
+=Q5eO
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-kubelet-dcr01s22.hswaw.net.key b/cluster/secrets/cipher/kube-kubelet-dcr01s22.hswaw.net.key
index c65c7b4..69fae62 100644
--- a/cluster/secrets/cipher/kube-kubelet-dcr01s22.hswaw.net.key
+++ b/cluster/secrets/cipher/kube-kubelet-dcr01s22.hswaw.net.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=j74g
+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
+=0TTn
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-kubelet-dcr01s24.hswaw.net.key b/cluster/secrets/cipher/kube-kubelet-dcr01s24.hswaw.net.key
index 56eb075..a9605e3 100644
--- a/cluster/secrets/cipher/kube-kubelet-dcr01s24.hswaw.net.key
+++ b/cluster/secrets/cipher/kube-kubelet-dcr01s24.hswaw.net.key
@@ -1,92 +1,92 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-KDID/Go=
-=YdtX
+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
+/wIU
+=WDTB
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-proxy.key b/cluster/secrets/cipher/kube-proxy.key
index 8f7395f..da70fbf 100644
--- a/cluster/secrets/cipher/kube-proxy.key
+++ b/cluster/secrets/cipher/kube-proxy.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-giBGiLkzqMcZAICmGiuID70n
-=nwnw
+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
+4s6lOSkYzHGO7aM3X9DiQLZgdPyq5A==
+=knRO
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-scheduler.key b/cluster/secrets/cipher/kube-scheduler.key
index c53cb3c..ec737bb 100644
--- a/cluster/secrets/cipher/kube-scheduler.key
+++ b/cluster/secrets/cipher/kube-scheduler.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-=KPDF
+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
+=g2EZ
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kube-serviceaccounts.key b/cluster/secrets/cipher/kube-serviceaccounts.key
index 7cf52e3..5379df9 100644
--- a/cluster/secrets/cipher/kube-serviceaccounts.key
+++ b/cluster/secrets/cipher/kube-serviceaccounts.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-2iNlo3a8aEsQGV9n1DZg4PsgLOwSxslWjLE=
-=Ulcf
+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
+=phL6
 -----END PGP MESSAGE-----
diff --git a/cluster/secrets/cipher/kubefront-apiserver.key b/cluster/secrets/cipher/kubefront-apiserver.key
index 3d89ba5..3930a49 100644
--- a/cluster/secrets/cipher/kubefront-apiserver.key
+++ b/cluster/secrets/cipher/kubefront-apiserver.key
@@ -1,91 +1,91 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-8lIl4rzIrKPVMWjRJgnJJAZ3mtwVfKduoa7VAcE=
-=noFA
+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
+=ZLuK
 -----END PGP MESSAGE-----
diff --git a/cluster/tools/BUILD b/cluster/tools/BUILD
index 8e0900e..d26f668 100644
--- a/cluster/tools/BUILD
+++ b/cluster/tools/BUILD
@@ -37,3 +37,12 @@
     data = ["@nixops//:bin", "//tools:secretstore"],
 )
 
+sh_binary(
+    name = "rook-s3cmd-config",
+    srcs = ["rook-s3cmd-config.sh"],
+    data = [
+        "@bazel_tools//tools/bash/runfiles",
+        "@com_github_stedolan_jq//:jq",
+        ":kubectl",
+    ],
+)
diff --git a/cluster/tools/calicoctl.sh b/cluster/tools/calicoctl.sh
index dc38998..30fe652 100755
--- a/cluster/tools/calicoctl.sh
+++ b/cluster/tools/calicoctl.sh
@@ -8,12 +8,12 @@
 fi
 
 ETCD_ENDPOINTS="https://bc01n01.hswaw.net:2379,https://bc01n01.hswaw.net:2379,https://bc01n01.hswaw.net:2379"
-ETCD_KEY_FILE="$hscloud_root/cluster/secrets/plain/kube-calico.key"
-ETCD_CERT_FILE="$hscloud_root/cluster/certs/kube-calico.crt"
-ETCD_CA_CERT_FILE="$hscloud_root/cluster/certs/ca.crt"
+ETCD_KEY_FILE="$hscloud_root/cluster/secrets/plain/etcd-calico.key"
+ETCD_CERT_FILE="$hscloud_root/cluster/certs/etcd-calico.cert"
+ETCD_CA_CERT_FILE="$hscloud_root/cluster/certs/ca-etcd.crt"
 
 if [ ! -f "$ETCD_KEY_FILE" ] ; then
-        secretstore decrypt "$hscloud_root/cluster/secrets/cipher/kube-calico.key" > "$ETCD_KEY_FILE"
+        secretstore decrypt "$hscloud_root/cluster/secrets/cipher/etcd-calico.key" > "$ETCD_KEY_FILE"
 fi
 
 export ETCD_ENDPOINTS
diff --git a/cluster/tools/install.sh b/cluster/tools/install.sh
index 08e3476..6f32fbb 100755
--- a/cluster/tools/install.sh
+++ b/cluster/tools/install.sh
@@ -15,9 +15,3 @@
         //cluster/tools:calicoctl \
         //cluster/tools:cfssl
 
-if [ ! -e /nix ] ; then
-    echo "WARNING: No Nix installation detected. nix-dependent tools (nixops) will not be built or available." 
-else
-    bazel build \
-            //cluster/tools:nixops
-fi
diff --git a/cluster/tools/rook-s3cmd-config b/cluster/tools/rook-s3cmd-config
deleted file mode 100755
index 3ec34e5..0000000
--- a/cluster/tools/rook-s3cmd-config
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-# Generates s3cmd config from rook.io CephObjectStoreUser secrets fetched from
-# Kubernetes apiserver. Accepts extra K8S_INTERNAL=1 environment variable flag
-# that generates config that connects to internal rgw service.
-#
-# Usage:
-#   ./rook-s3cmd-config USERNAME STORENAME CLUSTERNAME > config
-#   s3cmd -c config --region "STORENAME:default-placement" mb s3://test/
-
-set -e
-
-username="${1:-registry}"
-storename="${2:-waw-hdd-redundant-2-object}"
-clustername="${3:-ceph-waw2}"
-
-secret="$(kubectl get secrets rook-ceph-object-user-$storename-$username -n $clustername -o json)"
-accesskey="$(echo "$secret" | jq -r '.data.AccessKey' | base64 -d)"
-secretkey="$(echo "$secret" | jq -r '.data.SecretKey' | base64 -d)"
-
-if [[ ! -z "$K8S_INTERNAL" ]]; then
-    domain="rook-ceph-rgw-$storename.$clustername.svc.cluster.local"
-else
-    domain="object.$clustername.hswaw.net"
-fi
-
-cat <<EOF
-[default]
-access_key = $accesskey
-secret_key = $secretkey
-host_base = $domain
-host_bucket = $domain
-EOF
-
-if [[ ! -z "$K8S_INTERNAL" ]]; then
-    echo "use_https = False"
-fi
diff --git a/cluster/tools/rook-s3cmd-config.sh b/cluster/tools/rook-s3cmd-config.sh
new file mode 100755
index 0000000..703b597
--- /dev/null
+++ b/cluster/tools/rook-s3cmd-config.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+# Generates s3cmd config from rook.io CephObjectStoreUser secrets fetched from
+# Kubernetes apiserver. Accepts extra K8S_INTERNAL=1 environment variable flag
+# that generates config that connects to internal rgw service.
+#
+# Usage:
+#   bazel run //cluster/tools:rook-s3cmd-config > config
+#   s3cmd -c config --region "STORENAME:default-placement" mb s3://test/
+
+set -euo pipefail
+
+# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+  if [[ -f "$0.runfiles_manifest" ]]; then
+    export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+  elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+    export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+  elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+    export RUNFILES_DIR="$0.runfiles"
+  fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+  source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+  source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+  echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+  exit 1
+fi
+# endpaste
+
+kubectl=$(rlocation "hscloud/cluster/tools/kubectl")
+if [ -z "$kubectl" ]; then
+    echo "Could not find kubectl in runfiles" >&2
+    exit 1
+fi
+
+jq=$(rlocation "com_github_stedolan_jq/jq")
+if [ -z "$jq" ]; then
+    echo "Could not find jq in runfiles" >&2
+    exit 1
+fi
+
+username="${1}"
+storename="${2:-waw-hdd-redundant-3-object}"
+clustername="${3:-ceph-waw3}"
+
+if [ -z "$username" ]; then
+    echo "Usage: $0 <username>" >&2
+    exit 1
+fi
+
+
+secret="$($kubectl get secrets rook-ceph-object-user-$storename-$username -n $clustername -o json)"
+accesskey="$(echo "$secret" | $jq -r '.data.AccessKey' | base64 -d)"
+secretkey="$(echo "$secret" | $jq -r '.data.SecretKey' | base64 -d)"
+
+if [[ ! -z "${K8S_INTERNAL:-}" ]]; then
+    domain="rook-ceph-rgw-$storename.$clustername.svc.cluster.local"
+else
+    domain="object.$clustername.hswaw.net"
+fi
+
+cat <<EOF
+[default]
+access_key = $accesskey
+secret_key = $secretkey
+host_base = $domain
+host_bucket = $domain
+EOF
+
+if [[ ! -z "${K8S_INTERNAL:-}" ]]; then
+    echo "use_https = False"
+fi
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..c284030
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,37 @@
+{ ... }@args:
+
+with builtins;
+
+let
+  fix = f: let x = f x; in x;
+
+  readTree = import ./nix/readtree.nix {};
+
+  # Tracking nixos-unstable as of 2020-08-22.
+  nixpkgsCommit = "c59ea8b8a0e7f927e7291c14ea6cd1bd3a16ff38";
+  nixpkgsSrc = fetchTarball {
+    url = "https://github.com/NixOS/nixpkgs-channels/archive/${nixpkgsCommit}.tar.gz";
+    sha256 = "1ak7jqx94fjhc68xh1lh35kh3w3ndbadprrb762qgvcfb8351x8v";
+  };
+  nixpkgs = import nixpkgsSrc {
+    config.allowUnfree = true;
+    config.allowBroken = true;
+  };
+
+in fix (self: rec {
+  config = {
+    hscloud = self // {
+      root = ./.;
+    };
+    pkgs = nixpkgs;
+    pkgsSrc = nixpkgsSrc;
+
+    inherit (nixpkgs) lib stdenv;
+  };
+
+  bgpwtf = readTree config ./bgpwtf;
+  cluster = readTree config ./cluster;
+  ops = readTree config ./ops;
+
+  pkgs = nixpkgs;
+})
diff --git a/devtools/depotview/BUILD.bazel b/devtools/depotview/BUILD.bazel
new file mode 100644
index 0000000..908a629
--- /dev/null
+++ b/devtools/depotview/BUILD.bazel
@@ -0,0 +1,46 @@
+load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push")
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["main.go"],
+    importpath = "code.hackerspace.pl/hscloud/devtools/depotview",
+    visibility = ["//visibility:private"],
+    deps = [
+        "//devtools/depotview/proto:go_default_library",
+        "//devtools/depotview/service:go_default_library",
+        "//go/mirko:go_default_library",
+        "@com_github_golang_glog//:go_default_library",
+    ],
+)
+
+go_binary(
+    name = "depotview",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
+
+container_layer(
+    name = "layer_bin",
+    files = [
+        ":depotview",
+    ],
+    directory = "/devtools/",
+)
+
+container_image(
+    name = "runtime",
+    base = "@prodimage-bionic//image",
+    layers = [
+        ":layer_bin",
+    ],
+)
+
+container_push(
+    name = "push",
+    image = ":runtime",
+    format = "Docker",
+    registry = "registry.k0.hswaw.net",
+    repository = "devtools/depotview",
+    tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}",
+)
diff --git a/devtools/depotview/README.md b/devtools/depotview/README.md
new file mode 100644
index 0000000..e1faca4
--- /dev/null
+++ b/devtools/depotview/README.md
@@ -0,0 +1,24 @@
+depotview
+=========
+
+Git-as-a-service over gRPC. Useful to get read-only access to hscloud.
+
+Production
+----------
+
+There's a prod instance running at depotview.devtools-prod.svc.cluster.local.
+
+Development
+-----------
+
+    $ bazel run //devtools/depotview -- -hspki_disable
+    $ grpcurl -plaintext -d '{"ref": "master"}' 127.0.0.1:4200 depotview.DepotView.Resolve
+    {
+      "hash": "154baf1cf6ed99ae5b2849f512ea4d58dbbf199e",
+      "lastChecked": 1586377071253733703
+    }
+    $ grpcurl -plaintext -d '{"hash": "154baf1cf6ed99ae5b2849f512ea4d58dbbf199e", "path": "//README"}' 127.0.0.1:4200 depotview.DepotView.Read
+    {
+      "data": "SFNDbG...."
+    }
+
diff --git a/devtools/depotview/main.go b/devtools/depotview/main.go
new file mode 100644
index 0000000..7b4aed4
--- /dev/null
+++ b/devtools/depotview/main.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+	"flag"
+
+	"code.hackerspace.pl/hscloud/go/mirko"
+	"github.com/golang/glog"
+
+	pb "code.hackerspace.pl/hscloud/devtools/depotview/proto"
+	"code.hackerspace.pl/hscloud/devtools/depotview/service"
+)
+
+var (
+	flagRemote = "https://gerrit.hackerspace.pl/hscloud"
+)
+
+func main() {
+	flag.StringVar(&flagRemote, "git_remote", flagRemote, "Address of Git repository to serve")
+	flag.Parse()
+
+	m := mirko.New()
+	if err := m.Listen(); err != nil {
+		glog.Exitf("Listen(): %v", err)
+	}
+
+	s := service.New(flagRemote)
+	pb.RegisterDepotViewServer(m.GRPC(), s)
+
+	if err := m.Serve(); err != nil {
+		glog.Exitf("Serve(): %v", err)
+	}
+
+	<-m.Done()
+}
diff --git a/devtools/depotview/proto/BUILD.bazel b/devtools/depotview/proto/BUILD.bazel
new file mode 100644
index 0000000..47df920
--- /dev/null
+++ b/devtools/depotview/proto/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+
+proto_library(
+    name = "proto_proto",
+    srcs = ["depotview.proto"],
+    visibility = ["//visibility:public"],
+)
+
+go_proto_library(
+    name = "proto_go_proto",
+    compilers = ["@io_bazel_rules_go//proto:go_grpc"],
+    importpath = "code.hackerspace.pl/hscloud/devtools/depotview/proto",
+    proto = ":proto_proto",
+    visibility = ["//visibility:public"],
+)
+
+go_library(
+    name = "go_default_library",
+    embed = [":proto_go_proto"],
+    importpath = "code.hackerspace.pl/hscloud/devtools/depotview/proto",
+    visibility = ["//visibility:public"],
+)
diff --git a/devtools/depotview/proto/depotview.proto b/devtools/depotview/proto/depotview.proto
new file mode 100644
index 0000000..b948fae
--- /dev/null
+++ b/devtools/depotview/proto/depotview.proto
@@ -0,0 +1,58 @@
+syntax = "proto3";
+package depotview;
+option go_package = "code.hackerspace.pl/hscloud/devtools/depotview/proto";
+
+service DepotView {
+    // Resolve a git branch/tag/ref... into a commit hash.
+    rpc Resolve(ResolveRequest) returns (ResolveResponse);
+
+    // Resolve a gerrit change number into a git commit hash.
+    rpc ResolveGerritChange(ResolveGerritChangeRequest) returns (ResolveGerritChangeResponse);
+        
+    // Minimal file access API. It kinda stinks.
+    rpc Stat(StatRequest) returns (StatResponse);
+    rpc Read(ReadRequest) returns (stream ReadResponse);
+}
+
+message ResolveRequest {
+    string ref = 1;
+}
+
+message ResolveResponse {
+    string hash = 1;
+    int64 last_checked = 2;
+}
+
+message ResolveGerritChangeRequest {
+    int64 change = 1;
+}
+
+message ResolveGerritChangeResponse {
+    string hash = 1;
+    int64 last_checked = 2;
+}
+
+message StatRequest {
+    string hash = 1;
+    string path = 2;
+}
+
+message StatResponse {
+    enum Type {
+        TYPE_INVALID = 0;
+        TYPE_NOT_PRESENT = 1;
+        TYPE_FILE = 2;
+        TYPE_DIRECTORY = 3;
+    };
+    Type type = 1;
+}
+
+message ReadRequest {
+    string hash = 1;
+    string path = 2;
+}
+
+message ReadResponse {
+    // Chunk of data. Empty once everything has been sent over.
+    bytes data = 1;
+}
diff --git a/devtools/depotview/service/BUILD.bazel b/devtools/depotview/service/BUILD.bazel
new file mode 100644
index 0000000..056ec30
--- /dev/null
+++ b/devtools/depotview/service/BUILD.bazel
@@ -0,0 +1,31 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        "gerrit.go",
+        "service.go",
+    ],
+    importpath = "code.hackerspace.pl/hscloud/devtools/depotview/service",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//devtools/depotview/proto:go_default_library",
+        "@com_github_go_git_go_git_v5//:go_default_library",
+        "@com_github_go_git_go_git_v5//config:go_default_library",
+        "@com_github_go_git_go_git_v5//plumbing:go_default_library",
+        "@com_github_go_git_go_git_v5//plumbing/filemode:go_default_library",
+        "@com_github_go_git_go_git_v5//plumbing/object:go_default_library",
+        "@com_github_go_git_go_git_v5//storage:go_default_library",
+        "@com_github_go_git_go_git_v5//storage/memory:go_default_library",
+        "@com_github_golang_glog//:go_default_library",
+        "@org_golang_google_grpc//codes:go_default_library",
+        "@org_golang_google_grpc//status:go_default_library",
+    ],
+)
+
+go_test(
+    name = "go_default_test",
+    srcs = ["service_test.go"],
+    embed = [":go_default_library"],
+    deps = ["//devtools/depotview/proto:go_default_library"],
+)
diff --git a/devtools/depotview/service/gerrit.go b/devtools/depotview/service/gerrit.go
new file mode 100644
index 0000000..7dab9e6
--- /dev/null
+++ b/devtools/depotview/service/gerrit.go
@@ -0,0 +1,56 @@
+package service
+
+import (
+	"strconv"
+	"strings"
+)
+
+type gerritMeta struct {
+	patchSet int64
+	changeId string
+	commit   string
+}
+
+// parseGerritMetadata takes a NoteDB metadata entry and extracts info from it.
+func parseGerritMetadata(messages []string) *gerritMeta {
+	meta := &gerritMeta{}
+
+	for _, message := range messages {
+		for _, line := range strings.Split(message, "\n") {
+			line = strings.TrimSpace(line)
+			if len(line) == 0 {
+				continue
+			}
+
+			parts := strings.SplitN(line, ":", 2)
+			if len(parts) < 2 {
+				continue
+			}
+			k, v := parts[0], strings.TrimSpace(parts[1])
+
+			switch k {
+			case "Patch-set":
+				n, err := strconv.ParseInt(v, 10, 64)
+				if err != nil {
+					continue
+				}
+				meta.patchSet = n
+			case "Change-id":
+				meta.changeId = v
+			case "Commit":
+				meta.commit = v
+			}
+		}
+	}
+
+	if meta.patchSet == 0 {
+		return nil
+	}
+	if meta.changeId == "" {
+		return nil
+	}
+	if meta.commit == "" {
+		return nil
+	}
+	return meta
+}
diff --git a/devtools/depotview/service/service.go b/devtools/depotview/service/service.go
new file mode 100644
index 0000000..be5e743
--- /dev/null
+++ b/devtools/depotview/service/service.go
@@ -0,0 +1,285 @@
+package service
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"regexp"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/golang/glog"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+
+	git "github.com/go-git/go-git/v5"
+	"github.com/go-git/go-git/v5/config"
+	"github.com/go-git/go-git/v5/plumbing"
+	"github.com/go-git/go-git/v5/plumbing/filemode"
+	"github.com/go-git/go-git/v5/plumbing/object"
+	"github.com/go-git/go-git/v5/storage"
+	"github.com/go-git/go-git/v5/storage/memory"
+
+	pb "code.hackerspace.pl/hscloud/devtools/depotview/proto"
+)
+
+var (
+	reHash = regexp.MustCompile(`[a-f0-9]{40,64}`)
+)
+
+type Service struct {
+	remote string
+	storer storage.Storer
+
+	mu        sync.Mutex
+	repo      *git.Repository
+	lastFetch time.Time
+}
+
+func New(remote string) *Service {
+	return &Service{
+		remote: remote,
+		storer: memory.NewStorage(),
+	}
+}
+
+func (s *Service) ensureRepo(ctx context.Context) error {
+	// Clone repository if necessary.
+	// Use background context - we don't want this to get canceled.
+	if s.repo == nil {
+		glog.Infof("Cloning %q...", s.remote)
+		repo, err := git.CloneContext(context.Background(), s.storer, nil, &git.CloneOptions{
+			URL: s.remote,
+		})
+		if err != nil {
+			glog.Errorf("Clone(%q): %v", s.remote, err)
+			return status.Error(codes.Unavailable, "could not clone repository")
+		}
+		s.repo = repo
+		glog.Infof("Clone done.")
+	}
+
+	// We could've gotten canceled by now.
+	if err := ctx.Err(); err != nil {
+		return err
+	}
+
+	// Fetch if necessary.
+	if time.Since(s.lastFetch) > 10*time.Second {
+		err := s.repo.FetchContext(ctx, &git.FetchOptions{
+			RefSpecs: []config.RefSpec{
+				config.RefSpec("+refs/heads/*:refs/heads/*"),
+				config.RefSpec("+refs/changes/*:refs/changes/*"),
+			},
+			Force: true,
+		})
+		if err != nil && err != git.NoErrAlreadyUpToDate {
+			glog.Errorf("Fetch(): %v", err)
+		} else {
+			s.lastFetch = time.Now()
+		}
+
+	}
+
+	return nil
+}
+
+func (s *Service) Resolve(ctx context.Context, req *pb.ResolveRequest) (*pb.ResolveResponse, error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	if req.Ref == "" {
+		return nil, status.Error(codes.InvalidArgument, "ref must be set")
+	}
+
+	if err := s.ensureRepo(ctx); err != nil {
+		return nil, err
+	}
+
+	h, err := s.repo.ResolveRevision(plumbing.Revision(req.Ref))
+	switch {
+	case err == plumbing.ErrReferenceNotFound:
+		return &pb.ResolveResponse{Hash: "", LastChecked: s.lastFetch.UnixNano()}, nil
+	case err != nil:
+		return nil, status.Errorf(codes.Unavailable, "git resolve error: %v", err)
+	default:
+		return &pb.ResolveResponse{Hash: h.String(), LastChecked: s.lastFetch.UnixNano()}, nil
+	}
+}
+
+func (s *Service) ResolveGerritChange(ctx context.Context, req *pb.ResolveGerritChangeRequest) (*pb.ResolveGerritChangeResponse, error) {
+	if err := s.ensureRepo(ctx); err != nil {
+		return nil, err
+	}
+
+	// I'm totally guessing this, from these examples:
+	//    refs/changes/03/3/meta
+	//    refs/changes/77/77/meta
+	//    refs/changes/47/247/meta
+	// etc...
+	shard := fmt.Sprintf("%02d", req.Change%100)
+	metaRef := fmt.Sprintf("refs/changes/%s/%d/meta", shard, req.Change)
+
+	h, err := s.repo.ResolveRevision(plumbing.Revision(metaRef))
+	switch {
+	case err == plumbing.ErrReferenceNotFound:
+		return &pb.ResolveGerritChangeResponse{Hash: "", LastChecked: s.lastFetch.UnixNano()}, nil
+	case err != nil:
+		return nil, status.Errorf(codes.Unavailable, "git metadata resolve error: %v", err)
+	}
+
+	c, err := s.repo.CommitObject(*h)
+	if err != nil {
+		return nil, status.Errorf(codes.Unavailable, "git error: %v", err)
+	}
+
+	var messages []string
+	for {
+		messages = append([]string{c.Message}, messages...)
+
+		if len(c.ParentHashes) != 1 {
+			break
+		}
+
+		c, err = s.repo.CommitObject(c.ParentHashes[0])
+		if err != nil {
+			return nil, status.Errorf(codes.Unavailable, "git error: %v", err)
+		}
+	}
+
+	meta := parseGerritMetadata(messages)
+	if meta == nil {
+		return nil, status.Errorf(codes.Internal, "could not parse gerrit metadata for ref %q", metaRef)
+	}
+	return &pb.ResolveGerritChangeResponse{Hash: meta.commit, LastChecked: s.lastFetch.UnixNano()}, nil
+}
+
+func (s *Service) getFile(ctx context.Context, hash, path string, notFoundOkay bool) (*object.File, error) {
+	if !reHash.MatchString(hash) {
+		return nil, status.Error(codes.InvalidArgument, "hash must be valid full git hash string")
+	}
+	if path == "" {
+		return nil, status.Error(codes.InvalidArgument, "path must be set")
+	}
+
+	path = pathNormalize(path)
+	if path == "" {
+		return nil, status.Error(codes.InvalidArgument, "path must be a valid unix or depot-style path")
+	}
+
+	if err := s.ensureRepo(ctx); err != nil {
+		return nil, err
+	}
+
+	c, err := s.repo.CommitObject(plumbing.NewHash(hash))
+	switch {
+	case err == plumbing.ErrObjectNotFound:
+		return nil, status.Error(codes.NotFound, "hash not found")
+	case err != nil:
+		return nil, status.Errorf(codes.Unavailable, "git error: %v", err)
+	}
+
+	file, err := c.File(path)
+	switch {
+	case err == object.ErrFileNotFound && !notFoundOkay:
+		return nil, status.Error(codes.NotFound, "file not found")
+	case err == object.ErrFileNotFound && notFoundOkay:
+		return nil, nil
+	case err != nil:
+		return nil, status.Errorf(codes.Unavailable, "git error: %v", err)
+	}
+
+	return file, nil
+}
+
+func (s *Service) Stat(ctx context.Context, req *pb.StatRequest) (*pb.StatResponse, error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	file, err := s.getFile(ctx, req.Hash, req.Path, true)
+	if err != nil {
+		return nil, err
+	}
+
+	if file == nil {
+		return &pb.StatResponse{Type: pb.StatResponse_TYPE_NOT_PRESENT}, nil
+	}
+
+	switch {
+	case file.Mode == filemode.Dir:
+		return &pb.StatResponse{Type: pb.StatResponse_TYPE_DIRECTORY}, nil
+	case file.Mode.IsFile():
+		return &pb.StatResponse{Type: pb.StatResponse_TYPE_FILE}, nil
+	default:
+		return nil, status.Errorf(codes.Unimplemented, "unknown file type %o", file.Mode)
+	}
+}
+
+func (s *Service) Read(req *pb.ReadRequest, srv pb.DepotView_ReadServer) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	ctx := srv.Context()
+
+	file, err := s.getFile(ctx, req.Hash, req.Path, false)
+	if err != nil {
+		return err
+	}
+
+	reader, err := file.Reader()
+	if err != nil {
+		return status.Errorf(codes.Unavailable, "file read error: %v", err)
+	}
+	defer reader.Close()
+
+	for {
+		if ctx.Err() != nil {
+			return ctx.Err()
+		}
+
+		// 1 MB read
+		chunk := make([]byte, 16*1024)
+		n, err := reader.Read(chunk)
+		switch {
+		case err == io.EOF:
+			n = 0
+		case err != nil:
+			return status.Errorf(codes.Unavailable, "file read error: %v", err)
+		}
+
+		err = srv.Send(&pb.ReadResponse{Data: chunk[:n]})
+		if err != nil {
+			return err
+		}
+
+		if n == 0 {
+			break
+		}
+	}
+
+	return nil
+}
+
+func pathNormalize(path string) string {
+	leadingSlashes := 0
+	for _, c := range path {
+		if c != '/' {
+			break
+		}
+		leadingSlashes += 1
+	}
+
+	// Only foo/bar, /foo/bar, and //foo/bar paths allowed.
+	if leadingSlashes > 2 {
+		return ""
+	}
+	path = path[leadingSlashes:]
+
+	// No trailing slashes allowed.
+	if strings.HasSuffix(path, "/") {
+		return ""
+	}
+
+	return path
+}
diff --git a/devtools/depotview/service/service_test.go b/devtools/depotview/service/service_test.go
new file mode 100644
index 0000000..8ea0764
--- /dev/null
+++ b/devtools/depotview/service/service_test.go
@@ -0,0 +1,32 @@
+package service
+
+import (
+	"context"
+	"testing"
+
+	pb "code.hackerspace.pl/hscloud/devtools/depotview/proto"
+)
+
+func TestIntegration(t *testing.T) {
+	// TODO(q3k); bring up fake git
+	s := New("https://gerrit.hackerspace.pl/hscloud")
+	ctx := context.Background()
+
+	res, err := s.Resolve(ctx, &pb.ResolveRequest{Ref: "master"})
+	if err != nil {
+		t.Fatalf("Resolve(master): %v", err)
+	}
+
+	if len(res.Hash) != 40 {
+		t.Fatalf("Resolve returned odd hash: %q", res.Hash)
+	}
+
+	res2, err := s.Stat(ctx, &pb.StatRequest{Hash: res.Hash, Path: "//WORKSPACE"})
+	if err != nil {
+		t.Fatalf("Stat(//WORKSPACE): %v", err)
+	}
+
+	if want, got := pb.StatResponse_TYPE_FILE, res2.Type; want != got {
+		t.Fatalf("Stat(//WORKSPACE): got %v, want %v", got, want)
+	}
+}
diff --git a/devtools/gerrit/BUILD b/devtools/gerrit/BUILD
index d9170c0..406200e 100644
--- a/devtools/gerrit/BUILD
+++ b/devtools/gerrit/BUILD
@@ -1,10 +1,11 @@
-load("@io_bazel_rules_docker//container:container.bzl", "container_image")
+load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")
 
 container_image(
     name="with_plugins",
-    base="@gerrit-3.0.0//image",
+    base="@gerrit-3.0.8//image",
     files = [
         "//devtools/gerrit/gerrit-oauth-provider:gerrit-oauth-provider",
+        "@com_googlesource_gerrit_plugin_owners//owners:owners.jar",
     ],
     # we cannot drop it directly in /var/gerrit/plugins as that changes the
     # directory owner to 0:0 and then breaks the gerrit installer that wants
@@ -12,22 +13,18 @@
     directory = "/var/gerrit-plugins",
 )
 container_image(
-    name="3.0.0-r7",
+    name="3.0.8-r1",
     base=":with_plugins",
     files = [":entrypoint.sh"],
     directory = "/",
     entrypoint = ["/entrypoint.sh"],
 )
 
-genrule(
-    name = "push_latest",
-    srcs = [":3.0.0-r7"],
-    outs = ["version.sh"],
-    executable = True,
-    cmd = """
-        tag=3.0.0-r7
-        docker tag bazel/devtools/gerrit:$$tag registry.k0.hswaw.net/devtools/gerrit:$$tag
-        docker push registry.k0.hswaw.net/devtools/gerrit:$$tag
-        echo -ne "#!/bin/sh\necho Pushed $$tag.\n" > $(OUTS)
-    """,
+container_push(
+    name = "push",
+    image = ":3.0.8-r1",
+    format = "Docker",
+    registry = "registry.k0.hswaw.net",
+    repository = "devtools/gerrit",
+    tag = "3.0.8-r1",
 )
diff --git a/devtools/gerrit/kube/gerrit.libsonnet b/devtools/gerrit/kube/gerrit.libsonnet
index e36c7fa..ce2982d 100644
--- a/devtools/gerrit/kube/gerrit.libsonnet
+++ b/devtools/gerrit/kube/gerrit.libsonnet
@@ -38,7 +38,7 @@
             address: "gerrit@hackerspace.pl",
         },
 
-        tag: "3.0.0-r7",
+        tag: "3.0.8-r1",
         image: "registry.k0.hswaw.net/devtools/gerrit:" + cfg.tag,
         resources: {
             requests: {
diff --git a/devtools/hackdoc/BUILD.bazel b/devtools/hackdoc/BUILD.bazel
new file mode 100644
index 0000000..186b0ef
--- /dev/null
+++ b/devtools/hackdoc/BUILD.bazel
@@ -0,0 +1,55 @@
+load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push")
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        "helpers.go",
+        "main.go",
+        "markdown.go",
+    ],
+    importpath = "code.hackerspace.pl/hscloud/devtools/hackdoc",
+    visibility = ["//visibility:private"],
+    deps = [
+        "//devtools/depotview/proto:go_default_library",
+        "//devtools/hackdoc/config:go_default_library",
+        "//devtools/hackdoc/source:go_default_library",
+        "//go/mirko:go_default_library",
+        "//go/pki:go_default_library",
+        "@com_github_gabriel_vasile_mimetype//:go_default_library",
+        "@com_github_golang_glog//:go_default_library",
+        "@in_gopkg_russross_blackfriday_v2//:go_default_library",
+        "@org_golang_google_grpc//:go_default_library",
+    ],
+)
+
+go_binary(
+    name = "hackdoc",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
+
+container_layer(
+    name = "layer_bin",
+    files = [
+        ":hackdoc",
+    ],
+    directory = "/devtools/",
+)
+
+container_image(
+    name = "runtime",
+    base = "@prodimage-bionic//image",
+    layers = [
+        ":layer_bin",
+    ],
+)
+
+container_push(
+    name = "push",
+    image = ":runtime",
+    format = "Docker",
+    registry = "registry.k0.hswaw.net",
+    repository = "q3k/hackdoc",
+    tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}",
+)
diff --git a/devtools/hackdoc/README.md b/devtools/hackdoc/README.md
new file mode 100644
index 0000000..7bf556a
--- /dev/null
+++ b/devtools/hackdoc/README.md
@@ -0,0 +1,33 @@
+Hackdoc
+=======
+
+Hackdoc is a tool to automatically serve documentation based on a checkout of the [hscloud](/) source.
+
+Usage
+-----
+
+Any Markdown submitted to hscloud is visible via hackdoc. Simply go to https://hackdoc.hackerspace.pl/path/to/markdown.md to see it rendered.
+
+You can pass a `?ref=foo` URL parameter to a hackdoc URL to get it to render a particular vesrion of the hscloud monorepo. For example:
+
+- https://hackdoc.hackerspace.pl/?ref=master for the `master` branch
+- https://hackdoc.hackerspace.pl/?ref=change/249 for the the source code at change '249'
+
+Special Markdown
+----------------
+
+We should be accepting a Somewhat Standard Subset Of Markdown. For reference, we're using the [blackfriday](https://godoc.org/gopkg.in/russross/blackfriday.v2) library with [CommonExtensions](https://godoc.org/gopkg.in/russross/blackfriday.v2#CommonExtensions) enabled.
+
+In addition, we also support Table of Contents autorendering, just place the following anywhere in your document to render a TOC:
+
+    [TOC]
+
+
+Local Rendering
+---------------
+
+To run hackdoc locally on a filesystem checkout (ie. when working on docs, templates, or hackdoc itself), run:
+
+     bazel run //devtools/hackdoc  -- -hspki_disable -docroot /path/to/hscloud
+
+The output log should tell you where hackdoc just started listening at. Currently this is `127.0.0.1:8080` by default. You can change this by passing a `-pub_listen` flag, eg. `-pub_listen 127.0.0.1:4242`.
diff --git a/devtools/hackdoc/config/BUILD.bazel b/devtools/hackdoc/config/BUILD.bazel
new file mode 100644
index 0000000..c5052c7
--- /dev/null
+++ b/devtools/hackdoc/config/BUILD.bazel
@@ -0,0 +1,19 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["config.go"],
+    importpath = "code.hackerspace.pl/hscloud/devtools/hackdoc/config",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//devtools/hackdoc/source:go_default_library",
+        "@com_github_burntsushi_toml//:go_default_library",
+    ],
+)
+
+go_test(
+    name = "go_default_test",
+    srcs = ["config_test.go"],
+    embed = [":go_default_library"],
+    deps = ["@com_github_go_test_deep//:go_default_library"],
+)
diff --git a/devtools/hackdoc/config/config.go b/devtools/hackdoc/config/config.go
new file mode 100644
index 0000000..aba384b
--- /dev/null
+++ b/devtools/hackdoc/config/config.go
@@ -0,0 +1,139 @@
+package config
+
+import (
+	"context"
+	"fmt"
+	"html/template"
+	"strings"
+
+	"github.com/BurntSushi/toml"
+
+	"code.hackerspace.pl/hscloud/devtools/hackdoc/source"
+)
+
+// Config is a configuration concerning a given path of the source. It is built
+// from files present in the source, and from global configuration.
+type Config struct {
+	// DefaultIndex is the filenames that should attempt to be rendered if no exact path is given
+	DefaultIndex []string
+	// Templates are the templates available to render markdown files, keyed by template name.
+	Templates map[string]*template.Template
+
+	// Errors that occured while building this config (due to config file errors, etc).
+	Errors map[string]error
+}
+
+type configToml struct {
+	DefaultIndex []string                       `toml:"default_index"`
+	Templates    map[string]*configTomlTemplate `toml:"template"`
+}
+
+type configTomlTemplate struct {
+	Sources []string `toml:"sources"`
+}
+
+func parseToml(data []byte) (*configToml, error) {
+	var c configToml
+	err := toml.Unmarshal(data, &c)
+	if err != nil {
+		return nil, err
+	}
+	if c.Templates == nil {
+		c.Templates = make(map[string]*configTomlTemplate)
+	}
+	return &c, nil
+}
+
+func configFileLocations(path string) []string {
+	// Support for unix-style filesystem prefix (/foo/bar/baz) and
+	// perforce-depot-style prefix (//foo/bar/baz).
+	// Also support relative paths.
+	pathTrimmed := strings.TrimLeft(path, "/")
+	prefixLen := len(path) - len(pathTrimmed)
+	prefix := path[:prefixLen]
+	path = pathTrimmed
+	if len(prefix) > 2 {
+		return nil
+	}
+
+	// Turn path into possible directory names, including root.
+	path = strings.Trim(path, "/")
+	parts := strings.Split(path, "/")
+	if parts[0] != "" {
+		parts = append([]string{""}, parts...)
+	}
+
+	locations := []string{}
+	for i, _ := range parts {
+		p := strings.Join(parts[:i+1], "/")
+		p += "/hackdoc.toml"
+		p = prefix + strings.Trim(p, "/")
+		locations = append(locations, p)
+	}
+	return locations
+}
+
+func ForPath(ctx context.Context, s source.Source, path string) (*Config, error) {
+	if path != "//" {
+		path = strings.TrimRight(path, "/")
+	}
+
+	cfg := &Config{
+		Templates: make(map[string]*template.Template),
+		Errors:    make(map[string]error),
+	}
+
+	tomlPaths := configFileLocations(path)
+	for _, p := range tomlPaths {
+		file, err := s.IsFile(ctx, p)
+		if err != nil {
+			return nil, fmt.Errorf("IsFile(%q): %w", path, err)
+		}
+		if !file {
+			continue
+		}
+		data, err := s.ReadFile(ctx, p)
+		if err != nil {
+			return nil, fmt.Errorf("ReadFile(%q): %w", path, err)
+		}
+
+		c, err := parseToml(data)
+		if err != nil {
+			cfg.Errors[p] = err
+			continue
+		}
+
+		err = cfg.updateFromToml(ctx, p, s, c)
+		if err != nil {
+			return nil, fmt.Errorf("updating from %q: %w", p, err)
+		}
+	}
+
+	return cfg, nil
+}
+
+func (c *Config) updateFromToml(ctx context.Context, p string, s source.Source, t *configToml) error {
+	if t.DefaultIndex != nil {
+		c.DefaultIndex = t.DefaultIndex
+	}
+
+	for k, v := range t.Templates {
+		tmpl := template.New(k)
+
+		for _, source := range v.Sources {
+			data, err := s.ReadFile(ctx, source)
+			if err != nil {
+				c.Errors[p] = fmt.Errorf("reading template file %q: %w", source, err)
+				return nil
+			}
+			tmpl, err = tmpl.Parse(string(data))
+			if err != nil {
+				c.Errors[p] = fmt.Errorf("parsing template file %q: %w", source, err)
+				return nil
+			}
+		}
+		c.Templates[k] = tmpl
+	}
+
+	return nil
+}
diff --git a/devtools/hackdoc/config/config_test.go b/devtools/hackdoc/config/config_test.go
new file mode 100644
index 0000000..ba542fe
--- /dev/null
+++ b/devtools/hackdoc/config/config_test.go
@@ -0,0 +1,99 @@
+package config
+
+import (
+	"testing"
+
+	"github.com/go-test/deep"
+)
+
+func TestParse(t *testing.T) {
+	for _, test := range []struct {
+		name string
+		data string
+		want *configToml
+	}{
+		{
+			name: "normal config",
+			data: `
+				default_index = ["foo.md", "bar.md"]
+				[template.default]
+				sources = ["hackdoc/bar.html", "hackdoc/baz.html"]
+				[template.foo]
+				sources = ["foo/bar.html", "foo/baz.html"]
+			`,
+			want: &configToml{
+				DefaultIndex: []string{"foo.md", "bar.md"},
+				Templates: map[string]*configTomlTemplate{
+					"default": &configTomlTemplate{
+						Sources: []string{"hackdoc/bar.html", "hackdoc/baz.html"},
+					},
+					"foo": &configTomlTemplate{
+						Sources: []string{"foo/bar.html", "foo/baz.html"},
+					},
+				},
+			},
+		}, {
+			name: "empty config",
+			data: "",
+			want: &configToml{
+				DefaultIndex: nil,
+				Templates:    map[string]*configTomlTemplate{},
+			},
+		},
+	} {
+		t.Run(test.name, func(t *testing.T) {
+			got, err := parseToml([]byte(test.data))
+			if err != nil {
+				t.Fatalf("could not parse config: %w", err)
+			}
+			if diff := deep.Equal(test.want, got); diff != nil {
+				t.Fatal(diff)
+			}
+		})
+	}
+}
+
+func TestLocations(t *testing.T) {
+	for _, test := range []struct {
+		name string
+		path string
+		want []string
+	}{
+		{
+			name: "perforce-style path",
+			path: "//foo/bar/baz",
+			want: []string{"//hackdoc.toml", "//foo/hackdoc.toml", "//foo/bar/hackdoc.toml", "//foo/bar/baz/hackdoc.toml"},
+		}, {
+			name: "unix-style path",
+			path: "/foo/bar/baz",
+			want: []string{"/hackdoc.toml", "/foo/hackdoc.toml", "/foo/bar/hackdoc.toml", "/foo/bar/baz/hackdoc.toml"},
+		}, {
+			name: "relative-style path",
+			path: "foo/bar/baz",
+			want: []string{"hackdoc.toml", "foo/hackdoc.toml", "foo/bar/hackdoc.toml", "foo/bar/baz/hackdoc.toml"},
+		}, {
+			name: "root perforce-style path",
+			path: "//",
+			want: []string{"//hackdoc.toml"},
+		}, {
+			name: "root unix-style path",
+			path: "/",
+			want: []string{"/hackdoc.toml"},
+		}, {
+			name: "empty path",
+			path: "",
+			want: []string{"hackdoc.toml"},
+		}, {
+			name: "weird path",
+			path: "///what/is///this///",
+			want: nil,
+		},
+	} {
+		t.Run(test.name, func(t *testing.T) {
+			got := configFileLocations(test.path)
+			if diff := deep.Equal(test.want, got); diff != nil {
+				t.Fatal(diff)
+			}
+		})
+	}
+}
diff --git a/devtools/hackdoc/helpers.go b/devtools/hackdoc/helpers.go
new file mode 100644
index 0000000..a2bd93a
--- /dev/null
+++ b/devtools/hackdoc/helpers.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/golang/glog"
+)
+
+func handle404(w http.ResponseWriter, r *http.Request) {
+	logRequest(w, r, "404")
+	w.WriteHeader(http.StatusNotFound)
+	fmt.Fprintf(w, "404!\n")
+}
+
+func handle500(w http.ResponseWriter, r *http.Request) {
+	logRequest(w, r, "500")
+	w.WriteHeader(http.StatusNotFound)
+	fmt.Fprintf(w, "500 :(\n")
+}
+
+func logRequest(w http.ResponseWriter, r *http.Request, format string, args ...interface{}) {
+	result := fmt.Sprintf(format, args...)
+	glog.Infof("result: %s, remote: %q, ua: %q, referrer: %q, host: %q path: %q", result, r.RemoteAddr, r.Header.Get("User-Agent"), r.Header.Get("Referrer"), r.Host, r.URL.Path)
+}
diff --git a/devtools/hackdoc/main.go b/devtools/hackdoc/main.go
new file mode 100644
index 0000000..e460721
--- /dev/null
+++ b/devtools/hackdoc/main.go
@@ -0,0 +1,261 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"net/http"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"time"
+
+	"code.hackerspace.pl/hscloud/go/mirko"
+	"code.hackerspace.pl/hscloud/go/pki"
+	"github.com/golang/glog"
+	"google.golang.org/grpc"
+
+	dvpb "code.hackerspace.pl/hscloud/devtools/depotview/proto"
+	"code.hackerspace.pl/hscloud/devtools/hackdoc/config"
+	"code.hackerspace.pl/hscloud/devtools/hackdoc/source"
+)
+
+var (
+	flagListen              = "127.0.0.1:8080"
+	flagDocRoot             = ""
+	flagDepotViewAddress    = ""
+	flagHackdocURL          = ""
+	flagGitwebDefaultBranch = "master"
+
+	rePagePath = regexp.MustCompile(`^/([A-Za-z0-9_\-/\. ]*)$`)
+)
+
+func init() {
+	flag.Set("logtostderr", "true")
+}
+
+func main() {
+	flag.StringVar(&flagListen, "pub_listen", flagListen, "Address to listen on for HTTP traffic")
+	flag.StringVar(&flagDocRoot, "docroot", flagDocRoot, "Path from which to serve documents. Either this or depotview must be set")
+	flag.StringVar(&flagDepotViewAddress, "depotview", flagDepotViewAddress, "gRPC endpoint of depotview to serve from Git. Either this or docroot must be set")
+	flag.StringVar(&flagHackdocURL, "hackdoc_url", flagHackdocURL, "Public URL of hackdoc. If not given, autogenerate from listen path for dev purposes")
+	flag.StringVar(&source.FlagGitwebURLPattern, "gitweb_url_pattern", source.FlagGitwebURLPattern, "Pattern to sprintf to for URL for viewing a file in Git. First string is ref/rev, second is bare file path (sans //)")
+	flag.StringVar(&flagGitwebDefaultBranch, "gitweb_default_rev", flagGitwebDefaultBranch, "Default Git rev to render/link to")
+	flag.Parse()
+
+	if flagHackdocURL == "" {
+		flagHackdocURL = fmt.Sprintf("http://%s", flagListen)
+	}
+
+	if flagDocRoot == "" && flagDepotViewAddress == "" {
+		glog.Errorf("Either -docroot or -depotview must be set")
+	}
+	if flagDocRoot != "" && flagDepotViewAddress != "" {
+		glog.Errorf("Only one of -docroot or -depotview must be set")
+	}
+
+	m := mirko.New()
+	if err := m.Listen(); err != nil {
+		glog.Exitf("Listen(): %v", err)
+	}
+
+	var s *service
+	if flagDocRoot != "" {
+		path, err := filepath.Abs(flagDocRoot)
+		if err != nil {
+			glog.Exitf("Could not dereference path %q: %w", path, err)
+		}
+		glog.Infof("Starting in docroot mode for %q -> %q", flagDocRoot, path)
+
+		s = &service{
+			source: source.NewSingleRefProvider(source.NewLocal(path)),
+		}
+	} else {
+		glog.Infof("Starting in depotview mode (server %q)", flagDepotViewAddress)
+		conn, err := grpc.Dial(flagDepotViewAddress, pki.WithClientHSPKI())
+		if err != nil {
+			glog.Exitf("grpc.Dial(%q): %v", flagDepotViewAddress, err)
+		}
+		stub := dvpb.NewDepotViewClient(conn)
+		s = &service{
+			source: source.NewDepotView(stub),
+		}
+	}
+
+	mux := http.NewServeMux()
+	mux.HandleFunc("/", s.handler)
+	srv := &http.Server{Addr: flagListen, Handler: mux}
+
+	glog.Infof("Listening on %q...", flagListen)
+	go func() {
+		if err := srv.ListenAndServe(); err != nil {
+			glog.Error(err)
+		}
+	}()
+
+	<-m.Done()
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	srv.Shutdown(ctx)
+
+}
+
+type service struct {
+	source source.SourceProvider
+}
+
+func (s *service) handler(w http.ResponseWriter, r *http.Request) {
+	if r.Method != "GET" && r.Method != "HEAD" {
+		w.WriteHeader(http.StatusMethodNotAllowed)
+		fmt.Fprintf(w, "method not allowed")
+		return
+	}
+
+	ref := r.URL.Query().Get("ref")
+	if ref == "" {
+		ref = flagGitwebDefaultBranch
+	}
+
+	ctx := r.Context()
+	source, err := s.source.Source(ctx, ref)
+	switch {
+	case err != nil:
+		glog.Errorf("Source(%q): %v", ref, err)
+		handle500(w, r)
+		return
+	case source == nil:
+		handle404(w, r)
+		return
+	}
+
+	path := r.URL.Path
+
+	if match := rePagePath.FindStringSubmatch(path); match != nil {
+		req := &request{
+			w:      w,
+			r:      r,
+			ctx:    r.Context(),
+			ref:    ref,
+			source: source,
+		}
+		req.handlePage(match[1])
+		return
+	}
+	handle404(w, r)
+}
+
+type request struct {
+	w   http.ResponseWriter
+	r   *http.Request
+	ctx context.Context
+
+	ref    string
+	source source.Source
+	// rpath is the path requested by the client
+	rpath string
+}
+
+func (r *request) handle500() {
+	handle500(r.w, r.r)
+}
+
+func (r *request) handle404() {
+	handle404(r.w, r.r)
+}
+
+func (r *request) logRequest(format string, args ...interface{}) {
+	logRequest(r.w, r.r, format, args...)
+}
+
+func urlPathToDepotPath(url string) string {
+	// Sanitize request.
+	parts := strings.Split(url, "/")
+	for i, p := range parts {
+		// Allow last part to be "", ie, for a path to end in /
+		if p == "" {
+			if i != len(parts)-1 {
+				return ""
+			}
+		}
+
+		// net/http sanitizes this anyway, but we better be sure.
+		if p == "." || p == ".." {
+			return ""
+		}
+	}
+	path := "//" + strings.Join(parts, "/")
+
+	return path
+}
+
+func (r *request) handlePageAuto(dirpath string) {
+	cfg, err := config.ForPath(r.ctx, r.source, dirpath)
+	if err != nil {
+		glog.Errorf("could not get config for path %q: %w", dirpath, err)
+		r.handle500()
+		return
+	}
+	for _, f := range cfg.DefaultIndex {
+		fpath := dirpath + f
+		file, err := r.source.IsFile(r.ctx, fpath)
+		if err != nil {
+			glog.Errorf("IsFile(%q): %w", fpath, err)
+			r.handle500()
+			return
+		}
+
+		if file {
+			http.Redirect(r.w, r.r, "/"+fpath+"?ref="+r.ref, 302)
+			return
+		}
+	}
+	r.handle404()
+}
+
+func (r *request) handlePage(page string) {
+	r.rpath = urlPathToDepotPath(page)
+
+	if strings.HasSuffix(r.rpath, "/") {
+		// Directory path given, autoresolve.
+		dirpath := r.rpath
+		if r.rpath != "//" {
+			dirpath = strings.TrimSuffix(r.rpath, "/") + "/"
+		}
+		r.handlePageAuto(dirpath)
+		return
+	}
+
+	// Otherwise, try loading the file.
+	file, err := r.source.IsFile(r.ctx, r.rpath)
+	if err != nil {
+		glog.Errorf("IsFile(%q): %w", r.rpath, err)
+		r.handle500()
+		return
+	}
+
+	// File exists, render that.
+	if file {
+		parts := strings.Split(r.rpath, "/")
+		dirpath := strings.Join(parts[:(len(parts)-1)], "/")
+		// TODO(q3k): figure out this hack, hopefully by implementing a real path type
+		if dirpath == "/" {
+			dirpath = "//"
+		}
+
+		cfg, err := config.ForPath(r.ctx, r.source, dirpath)
+		if err != nil {
+			glog.Errorf("could not get config for path %q: %w", dirpath, err)
+			r.handle500()
+			return
+		}
+		r.handleFile(r.rpath, cfg)
+		return
+	}
+
+	// Otherwise assume directory, try all posibilities.
+	dirpath := r.rpath
+	if r.rpath != "//" {
+		dirpath = strings.TrimSuffix(r.rpath, "/") + "/"
+	}
+	r.handlePageAuto(dirpath)
+}
diff --git a/devtools/hackdoc/markdown.go b/devtools/hackdoc/markdown.go
new file mode 100644
index 0000000..5004642
--- /dev/null
+++ b/devtools/hackdoc/markdown.go
@@ -0,0 +1,157 @@
+package main
+
+import (
+	"bytes"
+	"html/template"
+	"net/url"
+	"strings"
+
+	"code.hackerspace.pl/hscloud/devtools/hackdoc/config"
+
+	"github.com/gabriel-vasile/mimetype"
+	"github.com/golang/glog"
+	"gopkg.in/russross/blackfriday.v2"
+)
+
+// renderMarkdown renders markdown to HTML, replacing all relative (intra-hackdoc) links with version that have ref set.
+func renderMarkdown(input []byte, ref string) []byte {
+	r := blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{
+		Flags: blackfriday.CommonHTMLFlags | blackfriday.TOC,
+	})
+
+	// master is the default branch - do not make special links for that, as
+	// that makes them kinda ugly.
+	if ref == "master" {
+		ref = ""
+	}
+
+	parser := blackfriday.New(blackfriday.WithRenderer(r), blackfriday.WithExtensions(blackfriday.CommonExtensions))
+	ast := parser.Parse(input)
+
+	// Render table of contents (raw HTML) into bytes.
+	var tocB bytes.Buffer
+	tocB.Write([]byte(`<div class="toc">`))
+	r.RenderHeader(&tocB, ast)
+	tocB.Write([]byte(`</div>`))
+	toc := tocB.Bytes()
+
+	var buf bytes.Buffer
+	buf.Write([]byte(`<div class="content">`))
+	// Render Markdown with some custom behaviour.
+	ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
+		// Fix intra-hackdoc links to contain ?ref=
+		if ref != "" && entering && node.Type == blackfriday.Link || node.Type == blackfriday.Image {
+			dest := string(node.Destination)
+			u, err := url.Parse(dest)
+			if err == nil && !u.IsAbs() {
+				q := u.Query()
+				q["ref"] = []string{ref}
+				u.RawQuery = q.Encode()
+				node.Destination = []byte(u.String())
+				glog.V(10).Infof("link fix %q -> %q", dest, u.String())
+			}
+		}
+		// Replace [TOC] anchor with a rendered TOC.
+		if entering && node.Type == blackfriday.Text && string(node.Literal) == "[TOC]" {
+			buf.Write(toc)
+			return blackfriday.GoToNext
+		}
+		return r.RenderNode(&buf, node, entering)
+	})
+	buf.Write([]byte(`</div>`))
+	r.RenderFooter(&buf, ast)
+	return buf.Bytes()
+}
+
+type pathPart struct {
+	Label string
+	Path  string
+}
+
+func (r *request) renderable(dirpath string) bool {
+	cfg, err := config.ForPath(r.ctx, r.source, dirpath)
+	if err != nil {
+		glog.Errorf("could not get config for path %q: %v", dirpath, err)
+		return false
+	}
+
+	for _, f := range cfg.DefaultIndex {
+		fpath := dirpath + "/" + f
+		file, err := r.source.IsFile(r.ctx, fpath)
+		if err != nil {
+			glog.Errorf("IsFile(%q): %v", fpath, err)
+			return false
+		}
+
+		if file {
+			return true
+		}
+	}
+
+	return false
+}
+
+func (r *request) handleFile(path string, cfg *config.Config) {
+	data, err := r.source.ReadFile(r.ctx, path)
+	if err != nil {
+		glog.Errorf("ReadFile(%q): %w", err)
+		r.handle500()
+		return
+	}
+
+	// TODO(q3k): do MIME detection instead.
+	if strings.HasSuffix(path, ".md") {
+		rendered := renderMarkdown([]byte(data), r.ref)
+
+		r.logRequest("serving markdown at %s, cfg %+v", path, cfg)
+
+		// TODO(q3k): allow markdown files to override which template to load
+		tmpl, ok := cfg.Templates["default"]
+		if !ok {
+			glog.Errorf("No default template found for %s", path)
+			// TODO(q3k): implement fallback template
+			r.w.Write(rendered)
+			return
+		}
+
+		pathInDepot := strings.TrimPrefix(path, "//")
+		pathParts := []pathPart{
+			{Label: "//", Path: "/"},
+		}
+		parts := strings.Split(pathInDepot, "/")
+		fullPath := ""
+		for i, p := range parts {
+			label := p
+			if i != len(parts)-1 {
+				label = label + "/"
+			}
+			fullPath += "/" + p
+			target := fullPath
+			if i != len(parts)-1 && !r.renderable("/"+fullPath) {
+				target = ""
+			}
+			pathParts = append(pathParts, pathPart{Label: label, Path: target})
+		}
+
+		vars := map[string]interface{}{
+			"Rendered":    template.HTML(rendered),
+			"Title":       path,
+			"Path":        path,
+			"PathInDepot": pathInDepot,
+			"PathParts":   pathParts,
+			"HackdocURL":  flagHackdocURL,
+			"WebLinks":    r.source.WebLinks(pathInDepot),
+		}
+		err = tmpl.Execute(r.w, vars)
+		if err != nil {
+			glog.Errorf("Could not execute template for %s: %v", err)
+		}
+
+		return
+	}
+
+	// Just serve the file.
+	mime := mimetype.Detect(data)
+	r.w.Header().Set("Content-Type", mime.String())
+	r.w.Write(data)
+}
diff --git a/devtools/hackdoc/source/BUILD.bazel b/devtools/hackdoc/source/BUILD.bazel
new file mode 100644
index 0000000..f7f09c6
--- /dev/null
+++ b/devtools/hackdoc/source/BUILD.bazel
@@ -0,0 +1,13 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        "source.go",
+        "source_depotview.go",
+        "source_local.go",
+    ],
+    importpath = "code.hackerspace.pl/hscloud/devtools/hackdoc/source",
+    visibility = ["//visibility:public"],
+    deps = ["//devtools/depotview/proto:go_default_library"],
+)
diff --git a/devtools/hackdoc/source/source.go b/devtools/hackdoc/source/source.go
new file mode 100644
index 0000000..73d8990
--- /dev/null
+++ b/devtools/hackdoc/source/source.go
@@ -0,0 +1,36 @@
+package source
+
+import "context"
+
+var (
+	FlagGitwebURLPattern = "https://gerrit.hackerspace.pl/plugins/gitiles/hscloud/+/%s/%s"
+)
+
+type Source interface {
+	IsFile(ctx context.Context, path string) (bool, error)
+	ReadFile(ctx context.Context, path string) ([]byte, error)
+	IsDirectory(ctx context.Context, path string) (bool, error)
+	WebLinks(fpath string) []WebLink
+}
+
+type WebLink struct {
+	Kind      string
+	LinkLabel string
+	LinkURL   string
+}
+
+type SourceProvider interface {
+	Source(ctx context.Context, rev string) (Source, error)
+}
+
+type singleRefProvider struct {
+	source Source
+}
+
+func (s *singleRefProvider) Source(ctx context.Context, rev string) (Source, error) {
+	return s.source, nil
+}
+
+func NewSingleRefProvider(s Source) SourceProvider {
+	return &singleRefProvider{s}
+}
diff --git a/devtools/hackdoc/source/source_depotview.go b/devtools/hackdoc/source/source_depotview.go
new file mode 100644
index 0000000..6a256be
--- /dev/null
+++ b/devtools/hackdoc/source/source_depotview.go
@@ -0,0 +1,126 @@
+package source
+
+import (
+	"context"
+	"fmt"
+	"strconv"
+	"strings"
+
+	dvpb "code.hackerspace.pl/hscloud/devtools/depotview/proto"
+)
+
+type DepotViewSourceProvider struct {
+	stub dvpb.DepotViewClient
+}
+
+func NewDepotView(stub dvpb.DepotViewClient) SourceProvider {
+	return &DepotViewSourceProvider{
+		stub: stub,
+	}
+}
+
+func changeRef(ref string) int64 {
+	ref = strings.ToLower(ref)
+	if !strings.HasPrefix(ref, "change/") && !strings.HasPrefix(ref, "cr/") {
+		return 0
+	}
+	n, err := strconv.ParseInt(strings.SplitN(ref, "/", 2)[1], 10, 64)
+	if err != nil {
+		return 0
+	}
+
+	return n
+}
+
+func (s *DepotViewSourceProvider) Source(ctx context.Context, ref string) (Source, error) {
+	var hash string
+	n := changeRef(ref)
+	if n != 0 {
+		res, err := s.stub.ResolveGerritChange(ctx, &dvpb.ResolveGerritChangeRequest{Change: n})
+		if err != nil {
+			return nil, err
+		}
+		hash = res.Hash
+	} else {
+		res, err := s.stub.Resolve(ctx, &dvpb.ResolveRequest{Ref: ref})
+		if err != nil {
+			return nil, err
+		}
+		hash = res.Hash
+	}
+
+	if hash == "" {
+		return nil, nil
+	}
+
+	return &depotViewSource{
+		stub:   s.stub,
+		hash:   hash,
+		change: n,
+	}, nil
+}
+
+type depotViewSource struct {
+	stub   dvpb.DepotViewClient
+	hash   string
+	change int64
+}
+
+func (s *depotViewSource) IsFile(ctx context.Context, path string) (bool, error) {
+	res, err := s.stub.Stat(ctx, &dvpb.StatRequest{
+		Hash: s.hash,
+		Path: path,
+	})
+	if err != nil {
+		return false, err
+	}
+	return res.Type == dvpb.StatResponse_TYPE_FILE, nil
+}
+
+func (s *depotViewSource) IsDirectory(ctx context.Context, path string) (bool, error) {
+	res, err := s.stub.Stat(ctx, &dvpb.StatRequest{
+		Hash: s.hash,
+		Path: path,
+	})
+	if err != nil {
+		return false, err
+	}
+	return res.Type == dvpb.StatResponse_TYPE_DIRECTORY, nil
+}
+
+func (s *depotViewSource) ReadFile(ctx context.Context, path string) ([]byte, error) {
+	var data []byte
+	srv, err := s.stub.Read(ctx, &dvpb.ReadRequest{
+		Hash: s.hash,
+		Path: path,
+	})
+	if err != nil {
+		return nil, err
+	}
+	for {
+		res, err := srv.Recv()
+		if err != nil {
+			return nil, err
+		}
+		if len(res.Data) == 0 {
+			break
+		}
+		data = append(data, res.Data...)
+	}
+	return data, nil
+}
+
+func (s *depotViewSource) WebLinks(fpath string) []WebLink {
+	gitURL := fmt.Sprintf(FlagGitwebURLPattern, s.hash, fpath)
+	links := []WebLink{
+		WebLink{Kind: "gitweb", LinkLabel: s.hash[:16], LinkURL: gitURL},
+	}
+
+	if s.change != 0 {
+		gerritLabel := fmt.Sprintf("change %d", s.change)
+		gerritLink := fmt.Sprintf("https://gerrit.hackerspace.pl/%d", s.change)
+		links = append(links, WebLink{Kind: "gerrit", LinkLabel: gerritLabel, LinkURL: gerritLink})
+	}
+
+	return links
+}
diff --git a/devtools/hackdoc/source/source_local.go b/devtools/hackdoc/source/source_local.go
new file mode 100644
index 0000000..feecd8b
--- /dev/null
+++ b/devtools/hackdoc/source/source_local.go
@@ -0,0 +1,77 @@
+package source
+
+import (
+	"context"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+)
+
+type LocalSource struct {
+	root string
+}
+
+func NewLocal(root string) Source {
+	return &LocalSource{
+		root: strings.TrimRight(root, "/"),
+	}
+}
+
+func (s *LocalSource) resolve(path string) (string, error) {
+	if !strings.HasPrefix(path, "//") {
+		return "", fmt.Errorf("invalid path %q, expected // prefix", path)
+	}
+	path = path[2:]
+	if strings.HasPrefix(path, "/") {
+		return "", fmt.Errorf("invalid path %q, expected // prefix", path)
+	}
+
+	return s.root + "/" + path, nil
+}
+
+func (s *LocalSource) IsFile(ctx context.Context, path string) (bool, error) {
+	path, err := s.resolve(path)
+	if err != nil {
+		return false, err
+	}
+	stat, err := os.Stat(path)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return false, nil
+		}
+		return false, fmt.Errorf("os.Stat(%q): %w", path, err)
+	}
+	return !stat.IsDir(), nil
+}
+
+func (s *LocalSource) ReadFile(ctx context.Context, path string) ([]byte, error) {
+	path, err := s.resolve(path)
+	if err != nil {
+		return nil, err
+	}
+	// TODO(q3k): limit size
+	return ioutil.ReadFile(path)
+}
+
+func (s *LocalSource) IsDirectory(ctx context.Context, path string) (bool, error) {
+	path, err := s.resolve(path)
+	if err != nil {
+		return false, err
+	}
+	stat, err := os.Stat(path)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return false, nil
+		}
+		return false, fmt.Errorf("os.Stat(%q): %w", path, err)
+	}
+	return stat.IsDir(), nil
+}
+
+func (s *LocalSource) WebLinks(fpath string) []WebLink {
+	gitURL := fmt.Sprintf(FlagGitwebURLPattern, "master", fpath)
+	return []WebLink{
+		WebLink{Kind: "gitweb", LinkLabel: "master", LinkURL: gitURL},
+	}
+}
diff --git a/devtools/hackdoc/tpl/base.html b/devtools/hackdoc/tpl/base.html
new file mode 100644
index 0000000..5fd861a
--- /dev/null
+++ b/devtools/hackdoc/tpl/base.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>hackdoc:{{ .Title }}</title>
+        {{ template "head" . }}
+    </head>
+    <body>
+        {{ template "body" . }}
+    </body>
+</html>
diff --git a/devtools/hackdoc/tpl/default.html b/devtools/hackdoc/tpl/default.html
new file mode 100644
index 0000000..6211e38
--- /dev/null
+++ b/devtools/hackdoc/tpl/default.html
@@ -0,0 +1,247 @@
+{{ define "head" }}
+<style type="text/css">
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	font-size: 100%;
+	font: inherit;
+	vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section {
+	display: block;
+}
+body {
+	line-height: 1;
+}
+ol, ul {
+	list-style: none;
+}
+blockquote, q {
+	quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}
+
+body {
+    font-size: 14px;
+    line-height: 1.25em;
+    background-color: #f0f0f0;
+}
+
+.wrapper {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    width: 100%;
+}
+
+.column {
+    width: 80em;
+    padding: 1rem 0 1rem 0;
+}
+
+.page {
+    background-color: #fefefe;
+    padding: 0.5rem 2rem 3rem 2rem;
+}
+
+.header {
+    font-size: 1.2em;
+    font-family: Consolas, monospace;
+    margin-top: 1rem;
+    padding: 0.5em 0 0.5em 0;
+    display: inline-flex;
+}
+
+.header a {
+    text-decoration: none;
+}
+.header a:hover {
+    text-decoration: underline;
+}
+
+.header span.red {
+    color: #b30014;
+}
+
+.header span.part {
+    color: #666;
+    padding-left: 0.2em;
+}
+
+.header span.part a {
+    color: rgb(27, 106, 203);
+}
+.header span.part a:visited {
+    color: rgb(27, 106, 203);
+}
+
+.footer {
+    font-size: 0.8em;
+    color: #ccc;
+    font-weight: 800;
+    font-family: helvetica, arial, sans-serif;
+    padding: 0.5em 1em 1em;
+    text-align: right;
+}
+
+.footer .left {
+    float: left;
+}
+
+.footer .right {
+    float: right;
+}
+
+.footer a {
+    color: #bbb;
+}
+
+h1,h2,h3,h4 {
+    font-family: helvetica, arial, sans-serif;
+}
+
+.content h1 {
+    font-size: 1.6em;
+    padding: 1em 0 0 0;
+    font-weight: 800;
+}
+
+.content h2 {
+    font-size: 1.3em;
+    padding: 0.8em 0 0 0;
+    color: #333;
+    font-weight: 800;
+}
+
+.content h3 {
+    font-size: 1.2em;
+    padding: 0.4em 0 0 0;
+    color: #444;
+}
+
+.content h4 {
+    font-size: 1.0em;
+    color: #555;
+}
+
+.content strong {
+    font-weight: 600;
+}
+
+.content code {
+    font-family: Consolas, monospace;
+    background-color: #f8f8f8;
+}
+
+.content pre {
+    background-color: #f8f8f8;
+    border: 1px solid #d8d8d8;
+    margin: 1em;
+    padding: 0.5em;
+    overflow: auto;
+}
+
+.content p {
+    margin-top: 0.8em;
+    line-height: 1.5em;
+}
+
+.content :not(li) > ul {
+    padding-top: 0.5em;
+    line-height: 1.5em;
+}
+
+.content ul li {
+    padding-left: 1em;
+}
+
+.content :not(li) > ul > li::before {
+    content: "•";
+    color: #333;;
+    display: inline-block;
+    width: 1em;
+    margin-left: -0.5em;
+}
+
+.content li > ul > li::before {
+    content: "â—¦";
+    color: #333;;
+    display: inline-block;
+    width: 1em;
+    margin-left: -0.5em;
+}
+
+.content img {
+    max-width: 90%;
+    margin: 1em auto 1em auto;
+    display: block;
+}
+
+.toc {
+    padding: .5em;
+    border: 1px solid #ddd;
+    background-color: #f8f8f8;
+    margin: 2em;
+    max-width: 30%;
+    font-size: 1em;
+    font-family: sans-serif;
+}
+
+.toc a {
+    text-decoration: none;
+}
+
+</style>
+{{ end }}
+{{ define "body" }}
+<div class="wrapper">
+    <div class="column">
+        <div class="page">
+            <div class="header">
+                <span class="red">hackdoc:</span>
+                {{ range .PathParts }}
+                    {{ if ne .Path "" }}
+                        <span class="part"><a href="{{ .Path }}">{{ .Label }}</a></span>
+                    {{ else }}
+                        <span class="part">{{ .Label }}</span>
+                    {{ end }}
+                {{ end }}
+                <span class="red" style="margin-left: 1em;">shortcuts:</span> <a href="/">root</a>, <a href="/cluster/doc">cluster docs</a>, <a href="/doc/codelabs">codelabs</a>
+            </div>
+            {{ .Rendered }}
+        </div>
+        <div class="footer">
+            <div class="left">
+                View in:
+                {{ range .WebLinks }}
+                <span class="muted">[{{ .Kind }} <a href="{{ .LinkURL }}">{{ .LinkLabel }}</a>]</span>
+                {{ end }}
+            </div>
+            <div class="right">Generated by <a href="{{ .HackdocURL }}/devtools/hackdoc">hackdoc</a>.</div>
+        </div>
+    </div>
+</div>
+{{ end }}
diff --git a/devtools/kube/depotview.libsonnet b/devtools/kube/depotview.libsonnet
new file mode 100644
index 0000000..179a0ad
--- /dev/null
+++ b/devtools/kube/depotview.libsonnet
@@ -0,0 +1,16 @@
+local mirko = import "../../kube/mirko.libsonnet";
+local kube = import "../../kube/kube.libsonnet";
+
+{
+    cfg:: {
+        image: "registry.k0.hswaw.net/devtools/depotview:1586695514-ac43b3edac4df7964cbe349f8e39f6346871ea3d",
+    },
+
+    component(cfg ,env):: mirko.Component(env, "depotview") {
+        local depotview = self,
+        cfg+: {
+            image: cfg.image,
+            container: depotview.GoContainer("main", "/devtools/depotview") {}
+        },
+    }
+}
diff --git a/devtools/kube/hackdoc.libsonnet b/devtools/kube/hackdoc.libsonnet
new file mode 100644
index 0000000..d849ebb
--- /dev/null
+++ b/devtools/kube/hackdoc.libsonnet
@@ -0,0 +1,31 @@
+local mirko = import "../../kube/mirko.libsonnet";
+local kube = import "../../kube/kube.libsonnet";
+
+{
+    cfg:: {
+        image: "registry.k0.hswaw.net/q3k/hackdoc:1600885335-2b8f3c4af735f193604ee2f9240e063071a62de6",
+        publicFQDN: error "public FQDN must be set",
+    },
+
+    component(cfg ,env):: mirko.Component(env, "hackdoc") {
+        local hackdoc = self,
+        cfg+: {
+            image: cfg.image,
+            container: hackdoc.GoContainer("main", "/devtools/hackdoc") {
+                command+: [
+                    "-depotview=depotview.devtools-prod.svc.cluster.local:4200",
+                    "-hackdoc_url=https://%s" % [cfg.publicFQDN],
+                    "-pub_listen=0.0.0.0:8080",
+                ],
+            },
+            ports+: {
+                publicHTTP: {
+                    public: {
+                        port: 8080,
+                        dns: cfg.publicFQDN,
+                    },
+                },
+            },
+        },
+    }
+}
diff --git a/devtools/kube/prod.jsonnet b/devtools/kube/prod.jsonnet
new file mode 100644
index 0000000..3bdccd2
--- /dev/null
+++ b/devtools/kube/prod.jsonnet
@@ -0,0 +1,37 @@
+local mirko = import "../../kube/mirko.libsonnet";
+local policies = import "../../kube/policies.libsonnet";
+
+local depotview = import "depotview.libsonnet";
+local hackdoc = import "hackdoc.libsonnet";
+local sourcegraph = import "sourcegraph.libsonnet";
+
+{
+    devtools(name):: mirko.Environment(name) {
+        local env = self,
+        local cfg = self.cfg,
+        
+        cfg+: {
+            depotview: depotview.cfg,
+            hackdoc: hackdoc.cfg {
+                publicFQDN: "hackdoc.hackerspace.pl",
+            },
+            sourcegraph: sourcegraph.cfg {
+                publicFQDN: "cs.hackerspace.pl",
+            },
+        },
+
+        components: {
+            depotview: depotview.component(cfg.depotview, env),
+            hackdoc: hackdoc.component(cfg.hackdoc, env),
+            // This is configurated manually through the web interface, q3k has an account
+            // and can create more administrative ones if needed.
+            sourcegraph: sourcegraph.component(cfg.sourcegraph, env),
+        },
+    },
+
+    prod: self.devtools("devtools-prod") {
+        local env = self,
+        // For SourceGraph's tini container mess.
+        policy: policies.AllowNamespaceMostlySecure(env.cfg.namespace),
+    },
+}
diff --git a/devtools/kube/sourcegraph.libsonnet b/devtools/kube/sourcegraph.libsonnet
new file mode 100644
index 0000000..c7e977f
--- /dev/null
+++ b/devtools/kube/sourcegraph.libsonnet
@@ -0,0 +1,113 @@
+local mirko = import "../../kube/mirko.libsonnet";
+local kube = import "../../kube/kube.libsonnet";
+
+// Deploy SourceGraph, a code serach tool. Its configuration is fully managed
+// within sourcegraph itself, including user accounts.
+
+{
+    cfg:: {
+        image: "sourcegraph/server:3.17.1",
+        publicFQDN: error "public FQDN must be set",
+        storageClassName: "waw-hdd-redundant-3",
+    },
+
+    component(cfg, env):: mirko.Component(env, "sourcegraph") {
+        local sourcegraph = self,
+        cfg+: {
+            image: cfg.image,
+            volumes+: {
+                data: kube.PersistentVolumeClaimVolume(sourcegraph.pvc.data),
+                etc: kube.PersistentVolumeClaimVolume(sourcegraph.pvc.etc),
+            },
+            securityContext: {
+                runAsUser: 0,
+                fsGroup: 0,
+            },
+            // This container fixes some permissions that Kubernetes volume mounts break.
+            initContainer: sourcegraph.Container("fixperms") {
+                image: "alpine:3",
+                volumeMounts_+: {
+                    data: { mountPath: "/var/opt/sourcegraph" },
+                },
+                ports_: {},
+                command: [
+                    "sh", "-c",
+                    "chmod 755 /var/opt/sourcegraph; chmod -R 700 /var/opt/sourcegraph/postgresql",
+                ],
+            },
+            container: sourcegraph.Container("main") {
+                volumeMounts_+: {
+                    data: { mountPath: "/var/opt/sourcegraph" },
+                    etc: { mountPath: "/etc/sourcegraph" },
+                },
+                resources: {
+                    requests: {
+                        cpu: "100m",
+                        memory: "1Gi",
+                    },
+                    limits: {
+                        cpu: "1",
+                        memory: "2Gi",
+                    },
+                },
+            },
+            ports+: {
+                publicHTTP: {
+                    public: {
+                        port: 7080,
+                        dns: cfg.publicFQDN,
+                        // Authenticate as 'Anonymous' user by default. This is done in tandem
+                        // with Sourcegraphs authenticate-by-http-header feature, and is a
+                        // workaround for the lack of a public view in the self-hosted free
+                        // version of Sourcegraph.
+                        // https://twitter.com/sqs/status/1272659451292422144
+                        setHeaders: ["X-Forwarded-User Anonymous"],
+                    },
+                },
+            },
+            extraPaths: [
+                {
+                    // Redirect anonymous user settings to a service that doesn't
+                    // have any endpoints/backends.
+                    path: "/users/Anonymous/settings",
+                    backend: { serviceName: sourcegraph.blocksvc.metadata.name, servicePort: 8080 },
+                },
+            ],
+        },
+
+        blocksvc: kube.Service(sourcegraph.makeName("blocksvc")) {
+            metadata+: sourcegraph.metadata,
+            spec+: {
+                selector: null,
+                ports: [{ port: 2137, targetPort: 2137 }],
+            },
+        },
+
+        pvc: {
+            data: kube.PersistentVolumeClaim(sourcegraph.makeName("data")) {
+                metadata+: sourcegraph.metadata,
+                spec+: {
+                    storageClassName: cfg.storageClassName,
+                    accessModes: [ "ReadWriteOnce" ],
+                    resources: {
+                        requests: {
+                            storage: "40Gi",
+                        },
+                    },
+                },
+            },
+            etc: kube.PersistentVolumeClaim(sourcegraph.makeName("etc")) {
+                metadata+: sourcegraph.metadata,
+                spec+: {
+                    storageClassName: cfg.storageClassName,
+                    accessModes: [ "ReadWriteOnce" ],
+                    resources: {
+                        requests: {
+                            storage: "4Gi",
+                        },
+                    },
+                },
+            },
+        },
+    }
+}
diff --git a/doc/codelabs/getting-started/img/gerrit-change-annotated.png b/doc/codelabs/getting-started/img/gerrit-change-annotated.png
new file mode 100644
index 0000000..8b1b352
--- /dev/null
+++ b/doc/codelabs/getting-started/img/gerrit-change-annotated.png
Binary files differ
diff --git a/doc/codelabs/getting-started/img/gerrit-change.png b/doc/codelabs/getting-started/img/gerrit-change.png
new file mode 100644
index 0000000..2eed80b
--- /dev/null
+++ b/doc/codelabs/getting-started/img/gerrit-change.png
Binary files differ
diff --git a/doc/codelabs/getting-started/img/gerrit-dashboard.png b/doc/codelabs/getting-started/img/gerrit-dashboard.png
new file mode 100644
index 0000000..91b2111
--- /dev/null
+++ b/doc/codelabs/getting-started/img/gerrit-dashboard.png
Binary files differ
diff --git a/doc/codelabs/getting-started/img/gerrit-gear-click.png b/doc/codelabs/getting-started/img/gerrit-gear-click.png
new file mode 100644
index 0000000..821345f
--- /dev/null
+++ b/doc/codelabs/getting-started/img/gerrit-gear-click.png
Binary files differ
diff --git a/doc/codelabs/getting-started/img/gerrit-ssh-key-paste.png b/doc/codelabs/getting-started/img/gerrit-ssh-key-paste.png
new file mode 100644
index 0000000..f51d290
--- /dev/null
+++ b/doc/codelabs/getting-started/img/gerrit-ssh-key-paste.png
Binary files differ
diff --git a/doc/codelabs/getting-started/img/gerrit-ssh-keys-click.png b/doc/codelabs/getting-started/img/gerrit-ssh-keys-click.png
new file mode 100644
index 0000000..47c9a56
--- /dev/null
+++ b/doc/codelabs/getting-started/img/gerrit-ssh-keys-click.png
Binary files differ
diff --git a/doc/codelabs/getting-started/your-first-change.md b/doc/codelabs/getting-started/your-first-change.md
new file mode 100644
index 0000000..93990d0
--- /dev/null
+++ b/doc/codelabs/getting-started/your-first-change.md
@@ -0,0 +1,195 @@
+Your first hscloud Change
+=========================
+
+This codelab will teach you how to send your first change to hscloud. We'll be modifying some files in your personal directory, so you won't bother anyone or break anything.
+
+[TOC]
+
+Prerequisites
+-------------
+
+hscloud is a git repository. If you're new to git, this might be slightly confusing to you, but you should still be able to follow along. In case you need brush up on that, links to relevant resources await you at the bottom of this document.
+
+As for software, you'll need to have git installed on your workstation.
+
+Gerrit concepts
+---------------
+
+We use **Gerrit** as a code review and code gating platform for hscloud. Every change to hscloud is submitted and reviewed there.
+
+Changes to hscloud in Gerrit are called, well, changes. **Every change is a single commit, and every commit correspons to a single change.** A change is submitted to the master branch by creating a Change Request (CR) in Gerrit. This CR will then be reviewed by owner(s) of codebase(s) that this CR touches, and then submitted to master. This model is somewhat similar to Pull/Merge Requests on other platforms like GitLab or GitHub, but vastly less complex due to the 1 CR == 1 commit mapping.
+
+hscloud does not use any branches, and instead follows a **single, rolling master**.
+
+Accessing Gerrit
+----------------
+
+Gerrit's main interface is available through the web, at https://gerrit.hackerspace.pl/. After signing, click the Gerrit logo to navigate to your dashboard, which should look something like this:
+
+![](img/gerrit-dashboard.png)
+
+To access Gerrit via Git/SSH, you'll need to upload your SSH key to the Gerrit settings panel.
+
+If you don't have an SSH key yet, generate one with the following command:
+
+    $ ssh-keygen -t ed25519     # and press return for every question
+    $ cat ~/.ssh/id_ed25519.pub # this is your public key
+
+Then, add the key via the Gerrit web interface.
+
+**1. Click the gear icon in the top right corner:**
+
+![](img/gerrit-gear-click.png)
+
+**2. Click 'SSH keys' in the sidebar:** 
+
+![](img/gerrit-ssh-keys-click.png)
+
+**3. Paste your SSH public key in the textbox and click 'Add new SSH key':**
+
+![](img/gerrit-ssh-key-paste.png)
+
+You should now be able to SSH into Gerrit. As Git access is also over SSH, this gives you access to Git as well.
+
+    $ ssh q3k@gerrit.hackerspace.pl
+      ****    Welcome to Gerrit Code Review    ****
+    
+      Hi q3k, you have successfully connected over SSH.
+    
+      Unfortunately, interactive shells are disabled.
+      To clone a hosted Git repository, use:
+    
+      git clone ssh://q3k@gerrit.hackerspace.pl/REPOSITORY_NAME.git
+    ^C
+    $
+
+Note that you should be using your Warsaw Hackerspace SSO user name. If that's different from your local workstation username, you'll have to specify `user@gerrit.hackerspace.pl` in all the following commands.
+
+Cloning hscloud and looking around
+----------------------------------
+
+Now, clone hscloud and take a look around:
+
+    $ git clone gerrit.hackerspace.pl:hscloud
+    Cloning into 'hscloud'...
+    remote: Counting objects: 162, done
+    remote: Finding sources: 100% (162/162)
+    remote: Total 5469 (delta 45), reused 5439 (delta 45)
+    Receiving objects: 100% (5469/5469), 15.25 MiB | 14.08 MiB/s, done.
+    Resolving deltas: 100% (2686/2686), done.
+    $ cd hscloud
+    $ ls
+    app  bgpwtf  BUILD  bzl  cluster  COPYING  dc  devtools  doc  env.fish  env.sh  gcp  go  hackdoc.toml  hswaw  kube  OWNERS  personal  README.md  third_party  tools  WORKSPACE
+    $
+
+If you've already cloned hscloud before (eg. over https), either clone it again or update your origin to point at `user@gerrit.hackerspace.pl:hscloud`.
+
+You can also use Gitiles (a part of Gerrit) to view hscloud in your web browser: https://gerrit.hackerspace.pl/plugins/gitiles/hscloud/+/refs/heads/master
+
+Any time you see a `//path/written/like/this`, `//` refers to the root of the hscloud checkout. For example, `//devtools/gerrit` contains all code related to our Gerrit instance. If you want to learn more about hscloud's directory structure, start at [//README.md](/README.md).
+
+Configuring git for Gerrit
+--------------------------
+
+There's just a bit more of one-time setup that you'll need to do in order to send changes to Gerrit:
+
+    $ git config user.email 'user@hackerspace.pl' # replace user with your SSO login name
+    $ curl -Lo .git/hooks/commit-msg https://gerrit.hackerspace.pl/tools/hooks/commit-msg
+    $ chmod +x .git/hooks/commit-msg
+
+This sets up the email for the repository to correspond to your Hackerspace email address, and sets up the Gerrit Change-Id autogeneration hook (more on that later). You only need to run this once for each hscloud clone.
+
+Changing something
+------------------
+
+We're ready to go! Every hscloud contributor can edit any file they want in `//personal/$user` without having to go through code review. Let's create a file there.
+
+    $ mkdir -p personal/$USER
+    $ cd personal/$USER
+    $ echo '**Hello, world!**' > hello.md
+
+Feel free to be more creative what the contents of this file. But remember - anything you submit to hscloud, or any CR you create, is visible to the entire world!
+
+Then, create a git commit containing your changes:
+
+    $ git add hello.md
+    $ git commit -m "personal: say hello"
+
+This is standard git stuff. However, if you take a closer look at the commit you've just created, you'll see a `Change-Id:` line has been added:
+
+    $ git log -1
+    commit 222a00a25adeadbeeff7ec0a409deadc0de49fb4
+    Author: Sergiusz Bazanski <q3k@hackerspace.pl>
+    Date:   Sun Apr 12 18:37:21 2020 +0200
+    
+        personal: say hello
+        
+        Change-Id: I161ca0deadcode86c9b1446b141ecf1421372d9c
+
+This `Change-Id` uniquely identifies Gerrit Changes and CRs. This means that no matter the content of the commit message, or the parent of the commit, Gerrit will be able to map your work to a CR. You'll see more of this in a bit, when we modify our change. But for now, first, let's push it to Gerrit. This step is likely the largest difference between Gerrit and other git-based code revie software:
+
+    $ git push origin HEAD:refs/for/master
+    [...]
+    remote: Resolving deltas: 100% (2/2)
+    remote: Processing changes: refs: 1, new: 1, done    
+    remote: 
+    remote: SUCCESS
+    remote: 
+    remote:   https://gerrit.hackerspace.pl/c/hscloud/+/XXXX personal: say hello [NEW]
+    remote: 
+
+In case you're confused: what's happening here is that you're pushing whatever you have at the top of your local git history to Gerrit. Gerrit will then look at all commits pushed, and create a new CR for every commit whose Change-Id it cannot match to an existing CR. New CRs will then get assigned a sequential CR number, which will be printed to the command line. You can either copy the full URL, or just the number and go to https://gerrit.hackerspace.pl/1234 (where 1234 is your CR number).
+
+Viewing your CR in Gerrit
+-------------------------
+
+This is how your CR should look like in the Gerrit UI:
+
+![](img/gerrit-change.png)
+
+This might look daunting - but don't panic. The Gerrut UI is very utilitarion, and once you get through it's somewhat-not-flat learning curve you'll be wishing you could use Gerrit everywhere.
+
+To help you understand what's going on, we've divided the view into multiple, labeled sections - and we'll go through them one by one.
+
+![](img/gerrit-change-annotated.png)
+
+- **Commit/Change data** - this should look familiar: it's the commit you've submitted. Because of the change/commit mapping, the commit message is also the main message of the CR, and is what your reviewers will see. Also take note of the **Reply** button - we will not use it today, but it is used to review code and to reply to code comments.
+- **Files** - this is a collapsed view of all files that are going to be affected by this change. To view individual files, click their file names, or the 'expand all files' button. You can also use keyboard shortcuts to navigate files quickly (press `?` on your keyboard to learn more).
+- **Metadata** - these are fields related to the CR but not strictly to the change itself. This is for instance: the owner/author of the change, the assigned reviewers, the repository/branch names, etc. You will notice the CR in the screenshot has a reviewer attached. Hopefully, yours won't, as personal changes should not trigger review requests. If it does, it means q3k still hasn't fixed this.
+- **Labels** - labels are a Gerrit construct that represent people's and system's votes to include your change in hscloud. Generally, all labels must have a positive value set in order for a CR to be submittable. As this is a personal directory change, you will automatically get a `Personal-Only` 'okay' label. Otherwise, you would get a `Code-Review` 'needed' label, which would have to be fulfilled by a vote before your change could be submitted.
+- **Changelog** - this is a unified history of this CR. Any metadata update or code review comment will be reflected in a history here. In addition, subsequent versions of your CR will appear here as '**patch sets**', numbered from 1 onwards.
+- **Actions** - these buttons, and the three-dots menu to the right, mostly serve to manage the lifecycle of the CR. You will notice a **Submit** button here - don't press it yet, though :). Some other buttons will appearify here when you're viewing a change in a different context, for instance a 'Code Review +1' button if you were reviewing the change instead.
+
+Updating your CR
+----------------
+
+To make this a bit more interesting, let's do one more quick thing before we submit our CR: update it! This will hopefully show you a bit more about how Gerrit maps changes to commits.
+
+    $ echo "I am a change." >> hello.md
+    $ git add hello.md
+    $ git commit --amend
+    $ git push origin HEAD:refs/for/master
+
+You should notice that the flow to update a CR is pretty symmetrical to the flow that was used to create it. Instead of adding another commit on top (polluting history), you just amend your existing commit with whatever changes you want. This also means you can use quite powerful git constructs like `rebase -i` to alter history (for instance, stack one CR on top of another!) and arrange things in a pretty way.
+
+Regardless, the Gerrit CR view should now show a new **patch set** appear in your change. Feel free to mess around more with this, or just press the **Submit** button when you're ready :).
+
+Observing your change
+---------------------
+
+You should now be able to pull from Gerrit again and observe that your commit is now on master:
+
+    $ git checkout master
+    $ git fetch origin master
+    $ git reset --hard origin/master
+    $ git log
+
+Naturally, your change should also be visible in [gitiles](https://gerrit.hackerspace.pl/plugins/gitiles/hscloud/+/master).
+
+Further steps
+-------------
+
+ - If you need a **Git refresher** - we highly recommend the [Git Visual Reference](https://marklodato.github.io/visual-git-guide/index-en.html)
+ - While this codelab showed you how to create and submit CRs, you didn't see anything about code review. Watch this space for a codelab about that.
+ - You should now be able to commit and change code. Watch this space for a link to a codelab on using Bazel or writing a simple microservice in Go.
+
diff --git a/doc/codelabs/index.md b/doc/codelabs/index.md
new file mode 100644
index 0000000..790447f
--- /dev/null
+++ b/doc/codelabs/index.md
@@ -0,0 +1,11 @@
+Codelabs
+========
+
+Codelabs are short, sweet and hands-on technical tutorials that teach you concepts related to hscloud and our infrastructure in general.
+
+Yes, there aren't many of these yet. Wanna change that? :)
+
+Getting started
+---------------
+
+- [**Your First Change**](getting-started/your-first-change.md) - how to use Gerrit and git to send your first change to hscloud, and an intro to personal directories. Using Gerrit can be somewhat confusing even (or especially) if you're used to Gitflow or GitHub.
diff --git a/doc/img/hscloud-smol.png b/doc/img/hscloud-smol.png
new file mode 100644
index 0000000..3aa3126
--- /dev/null
+++ b/doc/img/hscloud-smol.png
Binary files differ
diff --git a/doc/img/hscloud.png b/doc/img/hscloud.png
new file mode 100644
index 0000000..9e3c1a1
--- /dev/null
+++ b/doc/img/hscloud.png
Binary files differ
diff --git a/env.sh b/env.sh
index 27cf34f..7ef445f 100644
--- a/env.sh
+++ b/env.sh
@@ -21,6 +21,8 @@
     export hscloud_nixos=true
 fi
 
+alias bajzel=bazel
+
 gpg-unlock() {
     echo "test" | gpg2 --sign --batch --no-tty -o /dev/null
 }
diff --git a/games/factorio/modproxy/BUILD.bazel b/games/factorio/modproxy/BUILD.bazel
new file mode 100644
index 0000000..fb0c344
--- /dev/null
+++ b/games/factorio/modproxy/BUILD.bazel
@@ -0,0 +1,49 @@
+load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push")
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["main.go"],
+    importpath = "code.hackerspace.pl/hscloud/games/factorio/modproxy",
+    visibility = ["//visibility:private"],
+    deps = [
+        "//games/factorio/modproxy/modportal:go_default_library",
+        "//games/factorio/modproxy/proto:go_default_library",
+        "//go/mirko:go_default_library",
+        "@com_github_golang_glog//:go_default_library",
+        "@org_golang_google_grpc//codes:go_default_library",
+        "@org_golang_google_grpc//status:go_default_library",
+    ],
+)
+
+go_binary(
+    name = "modproxy",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
+
+container_layer(
+    name = "layer_bin",
+    files = [
+        ":modproxy",
+        "//games/factorio/modproxy/client:client",
+    ],
+    directory = "/games/factorio/modproxy/",
+)
+
+container_image(
+    name = "runtime",
+    base = "@prodimage-bionic//image",
+    layers = [
+        ":layer_bin",
+    ],
+)
+
+container_push(
+    name = "push",
+    image = ":runtime",
+    format = "Docker",
+    registry = "registry.k0.hswaw.net",
+    repository = "games/factorio/modproxy",
+    tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}",
+)
diff --git a/games/factorio/modproxy/README.md b/games/factorio/modproxy/README.md
new file mode 100644
index 0000000..ea580c7
--- /dev/null
+++ b/games/factorio/modproxy/README.md
@@ -0,0 +1,38 @@
+Factorio modproxy
+=================
+
+The modproxy is a microservice that caches Factorio mods.
+
+Usually, Factorio mods from the mod portal need credentials in order to be downloaded. As nobody is willing to share their credentials, or buy a HSWAW factorio license, this proxy got implemented.
+
+    .-------------------.        .----------.                  .----------.
+    | mods.factorio.com |<-------| modproxy |<-------------.---|.----------.
+    '-------------------'  HTTP  '----------'     gRPC     :----| factorio |
+                                      | Local              |   '| server   |
+                                      | files              |    '----------'
+                                      V                    |    .----------.
+                                   .-'''-.                 '----| Account  |
+                                   |-___-|                      | holder   |
+                                   | CAS | Cached mods          '----------'
+                                   '-___-'
+
+Factorio servers run a `client` binary that attempts to synchronize local mods with a specified intent of wanted Factorio mods. Any mods that are missing are downloaded from the modproxy over gRPC. Regardless of the success of the downloads, the modproxy client will then continue running the Factorio server.
+
+The modproxy, when asked for a mod (via a `ModProxy.Download` gRPC call), will either serve it (if it has a copy of it), or record that this selected mod was missing and store this request in memory.
+
+Then, when an Account Holder connects and calls `ModProxy.Mirror`, the modproxy will go through its saved list of pending mods to download, and use the credentials provided to download them.
+
+Deployment
+----------
+
+Factorio servers and the modproxy live in the `factorio` namespace on k0. Factorio servers created via jsonnet will automatically spawn a modproxy client on startup that will attempt to download whatever mods havve been specified in the jsonnet configuration (which is serialized to `config.pb.text`).
+
+Synchronizing Mods as an Account Holder
+---------------------------------------
+
+If you have a Factorio account, you can connect over to the modproxy to feed it any mods that it wanted to download but couldn't. Currently this is done via a manual grpcurl call, an admin client might be introduced at some later point:
+
+    kubectl -n factorio port-forward deployment/proxy 4200
+    grpcurl -plaintext -format text -d 'username: "q3k" token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"' 127.0.0.1:4200 modproxy.ModProxy.Mirror
+
+The reuslt will be empty if no mods had to be synchronized.
diff --git a/games/factorio/modproxy/client/BUILD.bazel b/games/factorio/modproxy/client/BUILD.bazel
new file mode 100644
index 0000000..66ee10f
--- /dev/null
+++ b/games/factorio/modproxy/client/BUILD.bazel
@@ -0,0 +1,22 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["client.go"],
+    importpath = "code.hackerspace.pl/hscloud/games/factorio/modproxy/client",
+    visibility = ["//visibility:private"],
+    deps = [
+        "//games/factorio/modproxy/modportal:go_default_library",
+        "//games/factorio/modproxy/proto:go_default_library",
+        "//go/pki:go_default_library",
+        "@com_github_gogo_protobuf//proto:go_default_library",
+        "@com_github_golang_glog//:go_default_library",
+        "@org_golang_google_grpc//:go_default_library",
+    ],
+)
+
+go_binary(
+    name = "client",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
diff --git a/games/factorio/modproxy/client/client.go b/games/factorio/modproxy/client/client.go
new file mode 100644
index 0000000..3e4d6ca
--- /dev/null
+++ b/games/factorio/modproxy/client/client.go
@@ -0,0 +1,164 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	"code.hackerspace.pl/hscloud/go/pki"
+	"github.com/gogo/protobuf/proto"
+	"github.com/golang/glog"
+	"google.golang.org/grpc"
+
+	"code.hackerspace.pl/hscloud/games/factorio/modproxy/modportal"
+	pb "code.hackerspace.pl/hscloud/games/factorio/modproxy/proto"
+)
+
+func init() {
+	flag.Set("logtostderr", "true")
+}
+
+var (
+	flagProxy        string
+	flagFactorioPath string
+	flagConfigPath   string
+)
+
+func main() {
+	flag.StringVar(&flagProxy, "proxy", "modproxy.factorio.svc.k0.hswaw.net:4200", "Address of modproxy service")
+	flag.StringVar(&flagFactorioPath, "factorio_path", "", "Path to factorio server root")
+	flag.StringVar(&flagConfigPath, "config_path", "config.pb.text", "Path to client config file")
+	flag.Parse()
+
+	conn, err := grpc.Dial(flagProxy, pki.WithClientHSPKI())
+	if err != nil {
+		glog.Exitf("Dial(%q): %v", flagProxy, err)
+		return
+	}
+
+	if flagFactorioPath == "" {
+		glog.Exitf("factorio_path must be set")
+	}
+
+	if flagConfigPath == "" {
+		glog.Exitf("config_path must be set")
+	}
+
+	configBytes, err := ioutil.ReadFile(flagConfigPath)
+	if err != nil {
+		glog.Exitf("could not read config: %v", err)
+	}
+	configString := string(configBytes)
+	config := &pb.ClientConfig{}
+	err = proto.UnmarshalText(configString, config)
+	if err != nil {
+		glog.Exitf("could not parse config: %v", err)
+	}
+
+	ctx := context.Background()
+	proxy := pb.NewModProxyClient(conn)
+
+	// mod name -> wanted mod version
+	managed := make(map[string]string)
+
+	for _, m := range config.Mod {
+		modPath := fmt.Sprintf("%s/mods/%s_%s.zip", flagFactorioPath, m.Name, m.Version)
+		_, err := os.Stat(modPath)
+		if err == nil {
+			glog.Infof("Mod %s/%s up to date, skipping.", m.Name, m.Version)
+			continue
+		}
+
+		i, err := modportal.GetMod(ctx, m.Name)
+		if err != nil {
+			glog.Errorf("Could not fetch info about %s/%s: %v", m.Name, m.Version, err)
+			continue
+		}
+
+		release := i.ReleaseByVersion(m.Version)
+		if release == nil {
+			glog.Errorf("%s/%s: version does not exist!", m.Name, m.Version)
+			continue
+		}
+
+		glog.Infof("Trying to download %s/%s (%s)...", m.Name, m.Version, release.SHA1)
+
+		err = downloadMod(ctx, proxy, m.Name, release.SHA1, modPath)
+		if err != nil {
+			glog.Errorf("%s/%s: could not download mod: %v", m.Name, m.Version, err)
+		} else {
+			glog.Infof("Mod %s/%s downloaded.", m.Name, m.Version)
+			managed[m.Name] = m.Version
+		}
+	}
+
+	glog.Infof("Cleaning up old versions of managed mods...")
+	for mn, mv := range managed {
+		modPath := fmt.Sprintf("%s/mods/%s_%s.zip", flagFactorioPath, mn, mv)
+		modGlob := fmt.Sprintf("%s/mods/%s_*.zip", flagFactorioPath, mn)
+		matches, err := filepath.Glob(modGlob)
+		if err != nil {
+			glog.Errorf("Could not find old versions of %q: %v", mn, err)
+			continue
+		}
+
+		for _, m := range matches {
+			// skip managed version
+			if m == modPath {
+				continue
+			}
+			glog.Infof("Deleting old version: %s", m)
+
+			err := os.Remove(m)
+			if err != nil {
+				glog.Errorf("Could not remove old version %q: %v", m, err)
+			}
+		}
+	}
+	glog.Infof("Done!")
+}
+
+func downloadMod(ctx context.Context, proxy pb.ModProxyClient, modName, sha1, dest string) error {
+	req := &pb.DownloadRequest{
+		ModName:  modName,
+		FileSha1: sha1,
+	}
+
+	stream, err := proxy.Download(ctx, req)
+	if err != nil {
+		return err
+	}
+
+	data := []byte{}
+
+	status := pb.DownloadResponse_STATUS_INVALID
+	for {
+		res, err := stream.Recv()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return err
+		}
+
+		if res.Status != pb.DownloadResponse_STATUS_INVALID {
+			status = res.Status
+		}
+
+		data = append(data, res.Chunk...)
+	}
+
+	switch status {
+	case pb.DownloadResponse_STATUS_OKAY:
+	case pb.DownloadResponse_STATUS_NOT_AVAILABLE:
+		return fmt.Errorf("version not available on proxy")
+	default:
+		return fmt.Errorf("invalid download status: %v", status)
+	}
+
+	return ioutil.WriteFile(dest, data, 0644)
+}
diff --git a/games/factorio/modproxy/main.go b/games/factorio/modproxy/main.go
new file mode 100644
index 0000000..30e41bf
--- /dev/null
+++ b/games/factorio/modproxy/main.go
@@ -0,0 +1,298 @@
+package main
+
+import (
+	"context"
+	"crypto/sha1"
+	"encoding/hex"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"regexp"
+	"strings"
+	"sync"
+	"time"
+
+	"code.hackerspace.pl/hscloud/go/mirko"
+	"github.com/golang/glog"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+
+	"code.hackerspace.pl/hscloud/games/factorio/modproxy/modportal"
+	pb "code.hackerspace.pl/hscloud/games/factorio/modproxy/proto"
+)
+
+func init() {
+	flag.Set("logtostderr", "true")
+}
+
+var (
+	flagCASDirectory string
+)
+
+func main() {
+	flag.StringVar(&flagCASDirectory, "cas_directory", "cas", "directory in which to store cached files")
+	flag.Parse()
+	m := mirko.New()
+	if err := m.Listen(); err != nil {
+		glog.Exitf("Listen(): %v", err)
+	}
+
+	srv := &service{
+		cache: make(map[string]*cacheEntry),
+	}
+
+	pb.RegisterModProxyServer(m.GRPC(), srv)
+
+	if err := m.Serve(); err != nil {
+		glog.Exitf("Serve(): %v", err)
+	}
+
+	<-m.Done()
+}
+
+var (
+	reSha1 = regexp.MustCompile(`^[a-f0-9]+$`)
+)
+
+func casPath(sha1 string) string {
+	sha1 = strings.ToLower(sha1)
+	if !reSha1.MatchString(sha1) {
+		return ""
+	}
+	return fmt.Sprintf("%s/%s", flagCASDirectory, sha1)
+}
+
+type service struct {
+	mu sync.Mutex
+
+	// cache of sha1 -> cache entry
+	cache map[string]*cacheEntry
+}
+
+type cacheEntry struct {
+	expires *time.Time
+	modName string
+
+	// found means that this is an entry confirmed on the mod portal
+	found bool
+	// mirrored means we are ready to serve this file to users
+	mirrored bool
+}
+
+func (s *service) Mirror(ctx context.Context, req *pb.MirrorRequest) (*pb.MirrorResponse, error) {
+
+	// build map of sha1->modName for needed downloads
+	modNames := make(map[string]string)
+	s.mu.Lock()
+	for sha, e := range s.cache {
+		if e == nil {
+			continue
+		}
+		if e.found == false {
+			continue
+		}
+		if e.mirrored == true {
+			continue
+		}
+
+		modNames[sha] = e.modName
+	}
+	s.mu.Unlock()
+
+	okays := make(map[string]bool)
+	errors := make(map[string]error)
+
+	for sha, modName := range modNames {
+		k := fmt.Sprintf("%s/%s", modName, sha)
+		mod, err := modportal.GetMod(ctx, modName)
+		if err != nil {
+			errors[k] = err
+			continue
+		}
+		release := mod.ReleaseBySHA1(sha)
+		if release == nil {
+			errors[k] = fmt.Errorf("could not find sha1 in modportal - deleted?")
+			continue
+		}
+
+		r, err := release.Download(ctx, req.Username, req.Token)
+		if err != nil {
+			errors[k] = fmt.Errorf("could not download: %v", err)
+			continue
+		}
+
+		path := casPath(sha)
+		pathIncoming := path + ".incoming"
+
+		out, err := os.Create(pathIncoming)
+		if err != nil {
+			errors[k] = fmt.Errorf("could not create file: %v", err)
+			continue
+		}
+		_, err = io.Copy(out, r)
+		if err != nil {
+			errors[k] = fmt.Errorf("could not save: %v", err)
+			continue
+		}
+		err = os.Rename(pathIncoming, path)
+		if err != nil {
+			errors[k] = fmt.Errorf("could not commit file: %v", err)
+			continue
+		}
+
+		okays[k] = true
+		s.cacheFeed(sha, modName, nil, true, true)
+	}
+
+	res := &pb.MirrorResponse{
+		ModsErrors: make(map[string]string),
+	}
+	for m, _ := range okays {
+		glog.Infof("Downloaded %q", m)
+		res.ModsOkay = append(res.ModsOkay, m)
+	}
+	for m, err := range errors {
+		glog.Errorf("Could not download %q: %v", m, err)
+		res.ModsErrors[m] = fmt.Sprintf("%v", err)
+	}
+
+	return res, nil
+}
+
+func (s *service) cacheGet(sha1 string) (hit, found, mirrored bool) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	entry, ok := s.cache[sha1]
+	if !ok || entry == nil {
+		return
+	}
+
+	if entry.expires != nil && time.Now().Before(*entry.expires) {
+		delete(s.cache, sha1)
+		return
+	}
+
+	hit = true
+	found = entry.found
+	mirrored = entry.mirrored
+
+	return
+}
+
+func (s *service) cacheFeed(sha1, modName string, expires *time.Time, found, mirrored bool) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	s.cache[sha1] = &cacheEntry{
+		expires:  expires,
+		modName:  modName,
+		found:    found,
+		mirrored: mirrored,
+	}
+}
+
+func (s *service) serve(req *pb.DownloadRequest, srv pb.ModProxy_DownloadServer) error {
+	cas := casPath(req.FileSha1)
+	if cas == "" {
+		// Invalid sha1? Fail.
+		return status.Error(codes.Aborted, "invalid sha1")
+	}
+
+	file, err := os.Open(cas)
+	if err != nil {
+		// not in CAS, update cache and fail
+		s.cacheFeed(req.FileSha1, req.ModName, nil, true, false)
+		return srv.Send(&pb.DownloadResponse{
+			Status: pb.DownloadResponse_STATUS_NOT_AVAILABLE,
+		})
+	}
+	defer file.Close()
+
+	err = srv.Send(&pb.DownloadResponse{
+		Status: pb.DownloadResponse_STATUS_OKAY,
+	})
+	if err != nil {
+		return err
+	}
+
+	buf := make([]byte, 1024*1024)
+	hash := sha1.New()
+
+	for {
+		n, err := file.Read(buf)
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return status.Errorf(codes.Unavailable, "error reading file: %v", err)
+		}
+		hash.Write(buf[:n])
+		err = srv.Send(&pb.DownloadResponse{
+			Chunk: buf[:n],
+		})
+		if err != nil {
+			return err
+		}
+	}
+
+	// entire file send, double-check shasum
+	sum := hex.EncodeToString(hash.Sum(nil))
+	if sum != req.FileSha1 {
+		glog.Errorf("CAS corruption: wanted %q, got %q", req.FileSha1, sum)
+		return status.Error(codes.Aborted, "CAS corruption")
+	}
+
+	return nil
+}
+
+func (s *service) Download(req *pb.DownloadRequest, srv pb.ModProxy_DownloadServer) error {
+	ctx := srv.Context()
+
+	modName := req.ModName
+	if modName == "" {
+		return status.Error(codes.InvalidArgument, "mod name must be set")
+	}
+	sha1 := req.FileSha1
+	if sha1 == "" {
+		return status.Error(codes.InvalidArgument, "sha1 must be set")
+	}
+	sha1 = strings.ToLower(sha1)
+	req.FileSha1 = sha1
+
+	cacheHit, found, mirrored := s.cacheGet(sha1)
+	if cacheHit {
+		if !found {
+			return status.Error(codes.NotFound, "sha1 not found for mod")
+		}
+		if !mirrored {
+			return srv.Send(&pb.DownloadResponse{
+				Status: pb.DownloadResponse_STATUS_NOT_AVAILABLE,
+			})
+		}
+
+		// we have the file, serve it
+		return s.serve(req, srv)
+	}
+
+	// cache not hit, check mod portal
+	mod, err := modportal.GetMod(ctx, modName)
+	if err != nil {
+		return err
+	}
+	release := mod.ReleaseBySHA1(sha1)
+
+	// release not found in mod portal, cache and answer
+	if release == nil {
+		expires := time.Now().Add(1 * time.Minute)
+		s.cacheFeed(sha1, modName, &expires, false, false)
+		return status.Error(codes.InvalidArgument, "sha1 not found for mod")
+	}
+
+	// we assume it's mirrored - the first cas serve will prove us wrong otherwise and
+	// update the cache.
+	s.cacheFeed(sha1, modName, nil, true, true)
+	// call ourselves again now that the cache is fed. computers - it's like magic!
+	return s.Download(req, srv)
+}
diff --git a/games/factorio/modproxy/modportal/BUILD.bazel b/games/factorio/modproxy/modportal/BUILD.bazel
new file mode 100644
index 0000000..051aec7
--- /dev/null
+++ b/games/factorio/modproxy/modportal/BUILD.bazel
@@ -0,0 +1,12 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["modportal.go"],
+    importpath = "code.hackerspace.pl/hscloud/games/factorio/modproxy/modportal",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@org_golang_google_grpc//codes:go_default_library",
+        "@org_golang_google_grpc//status:go_default_library",
+    ],
+)
diff --git a/games/factorio/modproxy/modportal/modportal.go b/games/factorio/modproxy/modportal/modportal.go
new file mode 100644
index 0000000..5e507fd
--- /dev/null
+++ b/games/factorio/modproxy/modportal/modportal.go
@@ -0,0 +1,98 @@
+package modportal
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+type Mod struct {
+	Name     string    `json:"name"`
+	Releases []Release `json:"releases"`
+}
+
+type Release struct {
+	DownloadURL string   `json:"download_url"`
+	FileName    string   `json:"file_name"`
+	Info        InfoJSON `json:"info_json"`
+	ReleasedAt  string   `json:"released_at"`
+	SHA1        string   `json:"sha1"`
+	Version     string   `json:"version"`
+}
+
+type InfoJSON struct {
+	Dependencies    []string `json:"dependencies"`
+	FactorioVersion string   `json:"factorio_json"`
+}
+
+func GetMod(ctx context.Context, name string) (*Mod, error) {
+	url := fmt.Sprintf("https://mods.factorio.com/api/mods/%s", url.PathEscape(name))
+
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, status.Errorf(codes.Internal, "NewRequest(%q): %v", url, err)
+	}
+
+	req = req.WithContext(ctx)
+	res, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, status.Errorf(codes.Unavailable, "Do(req{%q}): %v", url, err)
+	}
+	defer res.Body.Close()
+
+	if res.StatusCode != 200 {
+		return nil, status.Errorf(codes.Unavailable, "mod portal responded with code: %d", res.StatusCode)
+	}
+
+	mod := &Mod{}
+	err = json.NewDecoder(res.Body).Decode(mod)
+	if err != nil {
+		return nil, status.Errorf(codes.Internal, "could not decode mod portal JSON: %v", err)
+	}
+
+	return mod, nil
+}
+
+func (m *Mod) ReleaseBySHA1(sha1 string) *Release {
+	for _, r := range m.Releases {
+		if r.SHA1 == sha1 {
+			return &r
+		}
+	}
+	return nil
+}
+
+func (m *Mod) ReleaseByVersion(version string) *Release {
+	for _, r := range m.Releases {
+		if r.Version == version {
+			return &r
+		}
+	}
+	return nil
+}
+
+func (r *Release) Download(ctx context.Context, username, token string) (io.ReadCloser, error) {
+	url := fmt.Sprintf("https://mods.factorio.com/%s?username=%s&token=%s", r.DownloadURL, username, token)
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, status.Errorf(codes.Internal, "NewRequest(%q): %v", url, err)
+	}
+
+	req = req.WithContext(ctx)
+	res, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, status.Errorf(codes.Unavailable, "Do(req{%q}): %v", url, err)
+	}
+	if res.StatusCode != 200 {
+		res.Body.Close()
+		return nil, status.Errorf(codes.Unavailable, "mod portal responded with code: %d", res.StatusCode)
+	}
+
+	return res.Body, err
+}
diff --git a/games/factorio/modproxy/proto/BUILD.bazel b/games/factorio/modproxy/proto/BUILD.bazel
new file mode 100644
index 0000000..0c30b2c
--- /dev/null
+++ b/games/factorio/modproxy/proto/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+
+proto_library(
+    name = "proto_proto",
+    srcs = ["modproxy.proto"],
+    visibility = ["//visibility:public"],
+)
+
+go_proto_library(
+    name = "proto_go_proto",
+    compilers = ["@io_bazel_rules_go//proto:go_grpc"],
+    importpath = "code.hackerspace.pl/hscloud/games/factorio/modproxy/proto",
+    proto = ":proto_proto",
+    visibility = ["//visibility:public"],
+)
+
+go_library(
+    name = "go_default_library",
+    embed = [":proto_go_proto"],
+    importpath = "code.hackerspace.pl/hscloud/games/factorio/modproxy/proto",
+    visibility = ["//visibility:public"],
+)
diff --git a/games/factorio/modproxy/proto/modproxy.proto b/games/factorio/modproxy/proto/modproxy.proto
new file mode 100644
index 0000000..a4121b2
--- /dev/null
+++ b/games/factorio/modproxy/proto/modproxy.proto
@@ -0,0 +1,43 @@
+syntax = "proto3";
+package modproxy;
+option go_package = "code.hackerspace.pl/hscloud/games/factorio/modproxy/proto";
+
+service ModProxy {
+    rpc Mirror(MirrorRequest) returns (MirrorResponse);
+    rpc Download(DownloadRequest) returns (stream DownloadResponse);
+}
+
+message MirrorRequest {
+    // Authentication details to access the mod portal.
+    string username = 1;
+    string token = 2;
+}
+
+message MirrorResponse {
+    repeated string mods_okay = 1;
+    map<string, string> mods_errors = 2;
+}
+
+message DownloadRequest {
+    string mod_name = 1;
+    string file_sha1 = 2;
+}
+
+message DownloadResponse {
+    enum Status {
+        STATUS_INVALID = 0;
+        STATUS_OKAY = 1;
+        STATUS_NOT_AVAILABLE = 2;
+    };
+    Status status = 1;
+    bytes chunk = 2;
+}
+
+// Configuration for client (in text proto, so singular names in repeated fields)
+message ClientConfig {
+    message Mod {
+        string name = 1;
+        string version = 2;
+    }
+    repeated Mod mod = 1;
+}
diff --git a/go/OWNERS b/go/OWNERS
new file mode 100644
index 0000000..4960455
--- /dev/null
+++ b/go/OWNERS
@@ -0,0 +1,3 @@
+inherited: false
+owners:
+  - q3k
diff --git a/go/mirko/trace.go b/go/mirko/trace.go
index 33b4352..7ec4e37 100644
--- a/go/mirko/trace.go
+++ b/go/mirko/trace.go
@@ -12,7 +12,7 @@
 	tr, ok := trace.FromContext(ctx)
 	if !ok {
 		fmtd := fmt.Sprintf(f, args...)
-		glog.Info("[no trace] %v", fmtd)
+		glog.Infof("[no trace] %v", fmtd)
 		return
 	}
 	tr.LazyPrintf(f, args...)
diff --git a/go/pki/BUILD.bazel b/go/pki/BUILD.bazel
index 5bc7522..f2eae41 100644
--- a/go/pki/BUILD.bazel
+++ b/go/pki/BUILD.bazel
@@ -2,7 +2,10 @@
 
 go_library(
     name = "go_default_library",
-    srcs = ["grpc.go"],
+    srcs = [
+        "grpc.go",
+        "locate.go",
+    ],
     importpath = "code.hackerspace.pl/hscloud/go/pki",
     visibility = ["//visibility:public"],
     deps = [
diff --git a/go/pki/grpc.go b/go/pki/grpc.go
index 1720ad8..44099c0 100644
--- a/go/pki/grpc.go
+++ b/go/pki/grpc.go
@@ -20,7 +20,6 @@
 	"crypto/x509"
 	"flag"
 	"fmt"
-	"io/ioutil"
 	"strings"
 
 	"github.com/golang/glog"
@@ -210,18 +209,19 @@
 		return []grpc.ServerOption{}
 	}
 
-	serverCert, err := tls.LoadX509KeyPair(flagCertificatePath, flagKeyPath)
+	loc, err := loadCredentials()
+	if err != nil {
+		glog.Exitf("WithServerHSPKI: loadCredentials: %v", err)
+	}
+
+	serverCert, err := tls.X509KeyPair(loc.cert, loc.key)
 	if err != nil {
 		glog.Exitf("WithServerHSPKI: cannot load service certificate/key: %v", err)
 	}
 
 	certPool := x509.NewCertPool()
-	ca, err := ioutil.ReadFile(flagCAPath)
-	if err != nil {
-		glog.Exitf("WithServerHSPKI: cannot load CA certificate: %v", err)
-	}
-	if ok := certPool.AppendCertsFromPEM(ca); !ok {
-		glog.Exitf("WithServerHSPKI: cannot use CA certificate: %v", err)
+	if ok := certPool.AppendCertsFromPEM(loc.ca); !ok {
+		glog.Exitf("WithServerHSPKI: cannot use CA certificate")
 	}
 
 	creds := grpc.Creds(credentials.NewTLS(&tls.Config{
@@ -235,7 +235,15 @@
 	return []grpc.ServerOption{creds, interceptor}
 }
 
-func WithClientHSPKI() grpc.DialOption {
+type ClientHSPKIOption func(c *tls.Config)
+
+func OverrideServerName(name string) ClientHSPKIOption {
+	return func(c *tls.Config) {
+		c.ServerName = name
+	}
+}
+
+func WithClientHSPKI(opts ...ClientHSPKIOption) grpc.DialOption {
 	if !flag.Parsed() {
 		glog.Exitf("WithServerHSPKI called before flag.Parse!")
 	}
@@ -243,23 +251,30 @@
 		return grpc.WithInsecure()
 	}
 
-	certPool := x509.NewCertPool()
-	ca, err := ioutil.ReadFile(flagCAPath)
+	loc, err := loadCredentials()
 	if err != nil {
-		glog.Exitf("WithClientHSPKI: cannot load CA certificate: %v", err)
-	}
-	if ok := certPool.AppendCertsFromPEM(ca); !ok {
-		glog.Exitf("WithClientHSPKI: cannot use CA certificate: %v", err)
+		glog.Exitf("WithServerHSPKI: loadCredentials: %v", err)
 	}
 
-	clientCert, err := tls.LoadX509KeyPair(flagCertificatePath, flagKeyPath)
+	certPool := x509.NewCertPool()
+	if ok := certPool.AppendCertsFromPEM(loc.ca); !ok {
+		glog.Exitf("WithServerHSPKI: cannot use CA certificate")
+	}
+
+	clientCert, err := tls.X509KeyPair(loc.cert, loc.key)
 	if err != nil {
 		glog.Exitf("WithClientHSPKI: cannot load service certificate/key: %v", err)
 	}
 
-	creds := credentials.NewTLS(&tls.Config{
+	config := &tls.Config{
 		Certificates: []tls.Certificate{clientCert},
 		RootCAs:      certPool,
-	})
+	}
+
+	for _, opt := range opts {
+		opt(config)
+	}
+
+	creds := credentials.NewTLS(config)
 	return grpc.WithTransportCredentials(creds)
 }
diff --git a/go/pki/locate.go b/go/pki/locate.go
new file mode 100644
index 0000000..3b4ca29
--- /dev/null
+++ b/go/pki/locate.go
@@ -0,0 +1,108 @@
+package pki
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"github.com/golang/glog"
+)
+
+// DeveloperCredentialsLocation returns the path containing HSPKI credentials
+// on developer machines. These are provisioned by //cluster/prodaccess, and
+// are used if available.
+func DeveloperCredentialsLocation() (string, error) {
+	cfgDir, err := os.UserConfigDir()
+	if err != nil {
+		glog.Exitf("UserConfigDir: %w", err)
+	}
+
+	return fmt.Sprintf("%s/hspki", cfgDir), nil
+}
+
+// DeveloperCredentialsPrincipal returns the principal/DN for which the local
+// developer credentials are provisioned.
+func DeveloperCredentialsPrincipal() (string, error) {
+	creds, err := loadDeveloperCredentials()
+	if err != nil {
+		return "", fmt.Errorf("when loading developer credentials: %w", err)
+	}
+	pair, err := tls.X509KeyPair(creds.cert, creds.key)
+	if err != nil {
+		return "", fmt.Errorf("when loading developer client cert: %w", err)
+	}
+	cert, err := x509.ParseCertificate(pair.Certificate[0])
+	if err != nil {
+		return "", fmt.Errorf("when parsing developer client cert: %w", err)
+	}
+	return cert.Subject.CommonName, nil
+}
+
+type creds struct {
+	ca   []byte
+	cert []byte
+	key  []byte
+}
+
+func loadDeveloperCredentials() (*creds, error) {
+	path, err := DeveloperCredentialsLocation()
+	if err != nil {
+		return nil, fmt.Errorf("DeveloperCredentialsLocation: %w")
+	}
+
+	c := creds{}
+	for _, el := range []struct {
+		target *[]byte
+		path   string
+	}{
+		{&c.ca, path + "/" + "ca.crt"},
+		{&c.cert, path + "/" + "tls.crt"},
+		{&c.key, path + "/" + "tls.key"},
+	} {
+		data, err := ioutil.ReadFile(el.path)
+		if err != nil {
+			return nil, fmt.Errorf("ReadFile(%q): %w", el.path, err)
+		}
+		*el.target = data
+	}
+
+	return &c, nil
+}
+
+func loadFlagCredentials() (*creds, error) {
+	c := creds{}
+	for _, el := range []struct {
+		target *[]byte
+		path   string
+	}{
+		{&c.ca, flagCAPath},
+		{&c.cert, flagCertificatePath},
+		{&c.key, flagKeyPath},
+	} {
+		data, err := ioutil.ReadFile(el.path)
+		if err != nil {
+			return nil, fmt.Errorf("ReadFile(%q): %w", el.path, err)
+		}
+		*el.target = data
+	}
+
+	return &c, nil
+}
+
+func loadCredentials() (*creds, error) {
+	dev, err := loadDeveloperCredentials()
+	if err == nil {
+		return dev, nil
+	}
+	glog.Warningf("Could not load developer PKI credentials: %v", err)
+
+	fl, err := loadFlagCredentials()
+	if err == nil {
+		return fl, err
+	}
+	glog.Warningf("Could not load flag-defined PKI credentials: %v", err)
+
+	return nil, fmt.Errorf("could not load any credentials")
+}
diff --git a/hackdoc.toml b/hackdoc.toml
new file mode 100644
index 0000000..83eacea
--- /dev/null
+++ b/hackdoc.toml
@@ -0,0 +1,7 @@
+default_index = ["index.md", "readme.md", "README.md"]
+
+[template.default]
+sources = [
+    "//devtools/hackdoc/tpl/base.html",
+    "//devtools/hackdoc/tpl/default.html",
+]
diff --git a/hswaw/kube/ldapweb.libsonnet b/hswaw/kube/ldapweb.libsonnet
index af6320b..2bae6ae 100644
--- a/hswaw/kube/ldapweb.libsonnet
+++ b/hswaw/kube/ldapweb.libsonnet
@@ -4,7 +4,7 @@
 {
     cfg:: {
         # Manually built from code.hackerspace.pl/q3k/ldap-web-public.
-        image: "registry.k0.hswaw.net/q3k/ldap-web:1571402374",
+        image: "registry.k0.hswaw.net/q3k/ldap-web:1597515993",
         webFQDN: error "webFQDN must be set!",
     },
 
diff --git a/hswaw/kube/secrets/cipher/prod-frab-secret-key-base b/hswaw/kube/secrets/cipher/prod-frab-secret-key-base
index 91bf61e..e91bcf2 100644
--- a/hswaw/kube/secrets/cipher/prod-frab-secret-key-base
+++ b/hswaw/kube/secrets/cipher/prod-frab-secret-key-base
@@ -1,29 +1,40 @@
 -----BEGIN PGP MESSAGE-----
 
-hQEMAzhuiT4RC8VbAQf/ZDZIBtx7N6meZ06mC8M0U0ebokbMElobJjuqTfvUcI0/
-9q2k6zoZAfPlL1NMsHTPOVew2cguJAeSlsVPdG1grFDH6p7U2dR9h9Vjr6ui/hhf
-Es08p2akPjQemke01CBP5kPS/0RqIJzj0xSX1DdUtxFUmv+CiNLz6+iXAL9YCRml
-T2vAmkDnv1uLr/SR0V6L2lktX+v1PSA+LhNRBGI6K6yFVT78lr3pv0/icgDNznk1
-i6e+nl0kwEh6k+JlS6YeBvn2E21elJG3AjCxj+J5CyHE9Apmi09aQdSdoUFYm26K
-lRQtOMNiGJfS0Nz15y2jzPCZtcYHJU7SHqvufLFaIIUBDANcG2tp6fXqvgEIALuz
-pDQQAf6QrTB4F6ZeNKYryqEwk3Vhedikyp65HtE4nTTobWN+fwoZq3R+IOy1pQmz
-UPkYONPqrwzLHfjfyeWonHH7utk2trmGXEKG+Fg8Me3IKy+SE5xZfR1vxLeXEKz2
-umU3ueBnWrA08nssFS7PmRxR6mzRfAK2cOIwzk75EUvv0LwTwLuX/R5axaA2zVhy
-QDG3ydE6FPwBI0GlPWlXLRuvYXXTOGKXPW86fpB2CxxaejUYqWXa4F+ZFd2EiK03
-jL1cng+D3c2jLDhvZUaNZCams82YFWMb/hXjbRQvEjQVvqf+swApE1X6h9+UJZFj
-9uRh86lJhtlN3ToY9guFAgwD4gPJTlzrs+8BD/9otEsuN0pQ+Cx0fNj9pkVq8fzO
-Zvx2t3X9dfbgVIzAEkrSolncy4NCCeDsEYSw9ZtcTKKTM/V+kRdkT3GW5ZeetRSh
-fhaNsBu92LFrTO+zafngFetjOWodrxDC7SaHcum0WHGSqhk1IVUWx3fdzAnPzuZp
-T7fYGwbm4ah5x6ARlGxTEmQ+3MTPRs9iUjjUw6+3/2Xxc78llDSyKM8IzDB2dL8y
-cyxSJNeZCJIHjWdVu8VP10joL0QQCOrM/BaXm8vVIvQh0n1LumaAUE5PHVXzIU2k
-uXPs5THXnaoNKp3cbXUfJ2keKspZp0vQpatTlfXfNmADvzlUFTm6I/xWuuGlG0QV
-EyPV6VJ0sPpKPBOaJdy/C/ulyuwNYYErtN2bZ0GGIlDMqA7hOGkIvm64fUAggCxf
-lnU8w/0FlVQr/3REqMhh2nIkdwxcVktM1vt6ngai+StfR4QMAiwZX7co+A0QLbjl
-2wToJPt13d/vpjbYV39RzbG4JsK3Q9tly3pSt6GrlwXhzENnAfV1hfMmiQ3Dtacv
-BCYqLXrZQeEF5Xw+EOPqO4eyFxpPlKM849a8womOMwqyfuNR0R3u9/4ObW9yaX/2
-XQ1VGn120a0DaXwgIZWRkCkEX98slD6e/PwMGavlEfzNAug4zqEkjRlWKom+SysO
-3AZ3Div4J7Uc3VLUStJ5AWfZLLJNy/4ZxiMYqk2LJkwsOpIz+gyhPCxhBy0OpaAG
-i+iqLNtb52U4Dm0rUGV7fnFevfKewLd0cFaDuv1RBceZ2cIZdrCZjKfERCKwlqkI
-bap/o6/0ZyhmBhqSe6DkUjVUnsTM8ae76GkSnDVrZmueJaMDp5jchQ==
-=Dxzr
+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
+=hseZ
 -----END PGP MESSAGE-----
diff --git a/hswaw/kube/secrets/cipher/prod-frab-smtp-password b/hswaw/kube/secrets/cipher/prod-frab-smtp-password
index a84c5a1..b0deb6c 100644
--- a/hswaw/kube/secrets/cipher/prod-frab-smtp-password
+++ b/hswaw/kube/secrets/cipher/prod-frab-smtp-password
@@ -1,29 +1,40 @@
 -----BEGIN PGP MESSAGE-----
 
-hQEMAzhuiT4RC8VbAQf8CC+i4x0ElACCvRpCt8RptHQnf6Gf8gE7shr16EG+MUfJ
-u7R4f7BymphOyj4Q7twVI+3bqOg9tvNKFUx69afU+/kx9rgsiVu8DB44WrYh9LNE
-cpjhAsPkAwyx3X8GiTLU5psppfveTGCEUiNlV1n899/y8+Mf5aPV+AaRogpa2qj3
-5Ea9QozWkCKjucxDP+ahqVKAGsDb3q8BCi9hZO+KJKUqFhm6XxMLv0yrji1U+kmp
-RbebiVTEd5o3qltca4Npxy9X5zuYpHPwNc0bs7z9FeNZf3/lKYToBqHKRkBJvCZ+
-NqUz7qG45/gRNaoyAF+y4/Gjdm1vaUKG3cyKaLNHE4UBDANcG2tp6fXqvgEH/3Kv
-y6tt86kRcYT5cs4o7NjAEijYZXESjsMbXDhbBz2D2wZssuxzuuJVeZ2MP663XvRq
-2bsjA2BQF6rhEgvXd06Ztq3Xjju55lPampzYqWAOeb2BlZ3Y5xwnebwiogLa/iTQ
-WnK98ACvfEvoimgwASmBYDC3N4GLpQN8ehl2yvRNlrW+aeEBTchZay5gc/E+P+/n
-JfQZJAEGaLO83hF+zCXa8K7H7RNlqzC47iK0U5Oz2kbbCPoGdQoIkTR12GUmLIfy
-TJ4IK1UCKCQKmkW/1uScDnU68EUFOnvUw+YNTCHMJMrDTIVwORPARk9OPMOyURJ4
-HG6D/MbkA4NnhcCrYSqFAgwD4gPJTlzrs+8BD/9QTw0Bc2nNK1Kw78M8DU13fSyI
-gvV/xWULGf0l/1cmkwfJR+2RxpxuLzJ2Oj0iSTFhZTeAkM0FvEqcq/8ysDNPjv0C
-LysRy5jb3MNKdrLYbcNXjL6tBFJgCPANlUAEhBFZGAwo+VBjTcM5NeRiIDvkNNXK
-klP3M1LeoLDzIpKWpTZ0FWtgmZ3X1fvZBTNfIgdAIIHNu4H8Bo0H13MBOZsiNH88
-+0Gw8P0y7sJP+fEPCqzaC7T6mK9PUDM3OqDODlVdg5KoWkOulf5UcC19fj3waYBs
-EFB4GhEkeVm8dY6kmhYbFpciaRR20SIrWKhO8EgP82g8o8+8sVqKY9Kwy7RZmHKB
-5mk3GH9kAKZd1DUJvUTJoI0jbFEeLlqSD5WmGyC5zWHKwuTlPUTFuNlQ7ioe+Xc1
-Z98O+j4FoGrmb/wppxuGqjz4gZlCQDtEeGx6Cx84NaaFEA9aAv5l+vchT6kucC+D
-VkEUCOiCpl/qGc1fhCOVVlIYde0az0prYKHsvh2yh65XLFdBcP5mp5CS6TWoqmTi
-8InGrvNC+sZXTIXvn9LrfKvgvCzY+XHr05LIMrVfpf0Z2jbT4A6HKewp8whFVDCQ
-Ci3z9ibiuVgbh4fQ80L/s3gK8qbQmivcREbH1K3KbzLZ8KOKsB55FhBtXMj7ShMD
-8yeac8MzHwXPsRzR7tJwAQsHbAHrGAoxPSnpl71OVQuW0gyZmtRzoNp0gR1BQI9Y
-nhruw/OPMBfO+SRnQqLDsvYOjiBcuDRMWzy10qXhX1JPrbBRl7Fp39/CjQOxLXdn
-SmDQlxWivU2brYy8XUfa+VMU6Q4BwG62a6EV8dObSA==
-=xayU
+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
+=bRrv
 -----END PGP MESSAGE-----
diff --git a/hswaw/kube/secrets/cipher/prod-pretalx-s3.json b/hswaw/kube/secrets/cipher/prod-pretalx-s3.json
index 14b3728..9516d33 100644
--- a/hswaw/kube/secrets/cipher/prod-pretalx-s3.json
+++ b/hswaw/kube/secrets/cipher/prod-pretalx-s3.json
@@ -1,39 +1,50 @@
 -----BEGIN PGP MESSAGE-----
 
-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
-raRueENiGaWHl7qfOOFEwEIHzxAMNJqZ+0hr
-=vw9f
+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
+cb1vJvmBdiUYTKb7+JZkSGrpaLcj2wm1qwY=
+=h1eJ
 -----END PGP MESSAGE-----
diff --git a/hswaw/kube/secrets/cipher/prod-pretalx-smtp-password b/hswaw/kube/secrets/cipher/prod-pretalx-smtp-password
index c5cb1d4..8ee901d 100644
--- a/hswaw/kube/secrets/cipher/prod-pretalx-smtp-password
+++ b/hswaw/kube/secrets/cipher/prod-pretalx-smtp-password
@@ -1,29 +1,40 @@
 -----BEGIN PGP MESSAGE-----
 
-hQEMAzhuiT4RC8VbAQf/brjhAD6uqMj+nF+ZeVP0awNNfUCHIA+73XEpJXOEmWmn
-WsrKFAB9PDlMdlfM1Hsa9svNOhNCIMaxCzE10NdRiZBKenw64/mF6J2NBpNXor79
-beE/fm+BLS9p+55seBhvkXl+h5WjpOrupoFc2+cXiQl8Ht5bOm0Bj7fj912pyoAE
-hExsdlUCjRGoTFYnMQJS6tkvuLhwKVS3eK+tYOwceph4amFSP7py010gVX9Dy3JO
-qs6qCFOX0VzC1soGFzOfJlEdSy7ufo9meO57rf8iVI91iCgOQQi0vTUxhfqoaQww
-K4cR9w6rE4n4SGa3HnjCjJ3Utz22XhYZ9Gw4KDj3soUBDANcG2tp6fXqvgEH/2Hv
-QP3ZxwrS59+wfYPT8VSomK71xevAeVEP6yVoCVz5HkE7CCg630X59fuzuEeaRDMh
-BYrRIHk6e7iUApP8tifGy4C3ZVgAYu1wZ24uE/3CKQp3kQyXGZSivsC2iEJH+ME0
-zSMxSND0qPsZqBxk0AA55tzVheQMMCAKOdAqOSPnJetx9yfe0DNL1i9LH5jetQ5r
-ymd58nFrC6Xax7sas012N/59o2666daKWm1fisYhXSYh3BMeHs9Lmi9wjtkpuZF8
-IA93iWZO9OBXJuomXM4h07JvH/7N2Kp+OK21E4eSKO0YA/66dels6W6O4heZVrWJ
-jCVL+hDQrtN3DxXvQj+FAgwD4gPJTlzrs+8BD/9kQGi/3paqAw+Keuo0w8IAGR0W
-lvXisMbhCp5Ir58F+07yLJJuOBULxMksvZqOo71fCLR9PzVZG7ejmzoZ/z6sMiMX
-XBlrisILM3d8h4aF4FbN8OFX/3CDLXSNrkw5Ei/B3q1G3KEt9TeFMbF2Hioe+Dm5
-b2jZAV9g2vLXBgiKZoowN+9CEAk18AW5+jfHeqH5Z9bF+BV84J33y91NZxk0w3NB
-NkIg3Xp1xjB540edg7NVlvPUgtA9NnpmSQVdTyGEU5LWYq/yaGrfx9f9/FON8FaF
-jpwE1tizeiRx4gs2dZrV6br16Mx2tZhe3mpUOLgINq1XVrV2j6GLCi4sqqbLhunk
-lBs6LJz87gVs3+KqxqCKpRf4EcVQR+2kpNYEYG4mYGilsvl5wIBtr1d4LlyACuut
-BNatnyS+R5teahdUJuiDp/Fh0RoWiBdmSG5zt2e+/sj7ovKpX164LaZOtanvcjPm
-P/bklro2yeeKuAscGG/3FctAtdWMbYf+r/zSgd9INHX5bkm7cH8fKJ7HP4RJsPmO
-tMUefkf04jAnxTdRZVXCVJlHRKTFBdnvaaDQgUs1AIcHkb3RJ6wcavyt3bNGKWNz
-rr5q+euofddIuaJTORfnsi40+aF52fJtwGRhNulgYJYn+1a1n0RKRp6xG28fcEbh
-TkdfdNSudgAz0kFCbtJuAc6Jqk3t0yd7NqFudIyDz+Bhzj+fzcjqCSat+mUQerWQ
-c23NXo7svThWnN4EiioheDaG98hmJqvTiZ8Rx0gVmhvFnTGogKs4xgQ0t0LromJJ
-w0vP3kjTmOqC8/tCP8lMbyHQ5qYWmLYz/OaBBpU=
-=8rf3
+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
+2BNsAgqfrMutCDTFV10oCuvBgCh6pJVLli4Zxg==
+=GCsF
 -----END PGP MESSAGE-----
diff --git a/hswaw/kube/secrets/cipher/prod-telegram-token b/hswaw/kube/secrets/cipher/prod-telegram-token
index c6940fa..465e469 100644
--- a/hswaw/kube/secrets/cipher/prod-telegram-token
+++ b/hswaw/kube/secrets/cipher/prod-telegram-token
@@ -1,29 +1,40 @@
 -----BEGIN PGP MESSAGE-----
 
-hQEMAzhuiT4RC8VbAQf/QQrqNsv2Gi0+VobUb8VhVwkodh2/pe/fNdTPydKOW3fu
-nuyzq8YGIkE/2pH8XwBNrZp4CqlKp+cPt5UGf7xoISfq7k/iD9hK3c0uAO5+ncTH
-qTVcgmYwlppG5tPq5drjeZHB2RbD+p9ye8joC4+mVc4lTXiq93iDAGw8CV8zA4dT
-Xbxe/ZpA4RzYbUZ42PY6wTc7rjyTABtsy77ZCzykkSX6khYsXdEg3fmSaZPpKzSH
-SMv2eVtf2qmNBxsae3nytm7939nDZ1/YeHVfjA6/3vFNPPguLepv9OIoe3Z6xHgt
-ZJSk+XxOGv6RM4XQP6q/1zu4S/PgaeF+OOvgpbSS4oUBDANcG2tp6fXqvgEH/jgl
-z7DymRgxnWOU4N88A1lnvXOI06PKtLnPhl6KMybwLVs4hoZsRb1bRQR+NN8HhTEZ
-xmufq1jIMgFORZNHuWbBCM40R8YydxOXJqfa0DGExQcdzhCCrFE26xq/NpiYBjQ8
-nSCPIGPVT5/Vx6YtuUZHtNMuWeoQV7b9T9rJdAVtswLvGF63UZTC54QLVP31E6E5
-2/7p4XagAwRt+qHQB4uqfU6xLYlmwA8KCzB/YHrB8NcDb3PwNl7ULCaCq+Iv01TS
-SQOqjiYU7YJDLxRcyj4f7R02VLQg+WRYxHNpn3TcHxf1HlKovxfX1w4XcaWuMHyh
-lvIHEXnUocs1ywl6NG2FAgwD4gPJTlzrs+8BD/0RWiUFf1oFDO+Me3yxIpQAhDMm
-aF19LkqJUcIZJlgyLOF9VWaVR6wskBiaokLLdsDh57LH3e4ylt9TFAVNoTZesyhR
-4op+aiK0POSfxc6PCHS2LXVx9xMpg8+Ee4gLRL4PhiTeou8xWHtRSDA5YmLf9EuS
-xDRW/u1ZZGkj7KeE00rnjZQEoWih7fSIyyAfK18kym1KDTDsaIddSLVv7+e8xeTJ
-wk46mNJGcEtGIiyEXVxx3wMgQWlWS16/nsgGr06WpK+UA+HxfPt5Fsgd7juGdAlV
-NA2Tz/R92Zo9rL7gPEgNEv9XAAK0d8v4R0s9AcsVeGiFhiv7sbm/zSn6b9GZQs6k
-Kls35zteybLUp5DMXboL0ygfcVu2wB08hy1W+ozwHt/7Hn24JS59NS+TmhbhAih5
-tdm32gv2pg7BJzsczBrMbGfHdL31cPiIFC8DQW7Vw7YSFyGBpmdq05HehZ2caFG7
-5kxAzDPwY20oy0eeZbLr/eXIZtbMil9B48Alylbpc7hV8mlw67FuI3pjKjq49e/o
-qbGxzdtdvvsdrTLhEZgvBtRVELq3nLkSSDLbWeck3UfsS71ccS+i0adk6brCGVsL
-gC2BtcBrNVK31jwcgNf1uuNmdCAlraEAWOojszi6RiXi8L0vpFcb/f/ulg2X5cTG
-N6PgAGEBplwD6m+bLtJ9ARaIrzMudXvIA/oInY9HoBYhYYRiVcPaCnwn+Fc3A+9+
-msL8wwZPuvGoHWQYDMxdAstV6ElEgKv/a9NwQS108wcYWMBun6fsdcdG4F94ZDis
-/0W0CnI1OIfave1VEzi+uluzpkTphHOckPDiysIrj/1wyA+j2IXWoRm5w7g=
-=RDnh
+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
+=wOGi
 -----END PGP MESSAGE-----
diff --git a/hswaw/kube/secrets/cipher/prod-twilio-token b/hswaw/kube/secrets/cipher/prod-twilio-token
index 518dbe8..d65b61c 100644
--- a/hswaw/kube/secrets/cipher/prod-twilio-token
+++ b/hswaw/kube/secrets/cipher/prod-twilio-token
@@ -1,29 +1,40 @@
 -----BEGIN PGP MESSAGE-----
 
-hQEMAzhuiT4RC8VbAQf7BmN7B04eFrAVGzx4namIaZgtd3gnElKNCnvR5KXjF3wr
-rU5EhPWYN4z3m6UQdEgi5Q7P7LOnq6+k4nS5uH0OlAhV6qKnG5d0XshfuQCdmyNx
-PtYi0jwQf2dzkQNOHRmm//SAEfN3owlRl38CzYUo9Qv4et8LYcDqpl71blMAZcVo
-Q5qEMigiLuWS3Ri/18JU7riykPpsnJcEu2nzn//YtxRdS5Xf3AgcS76z27bJUXTm
-OnGrXsvKSrh8KFw9+Zrnl4zI1W5ivWjBwv7IV4148DKY7DaSSvlT+T40/FdsKJdK
-E1VVP4P8S6Zcyg+AgEAN/T2zxQy4eUYBI+y7uee2JoUBDANcG2tp6fXqvgEH/1Uf
-QnX4bSDL7eF8shjQquIPJigCgXJHH66YLR4YWPuQUkjut80IcdQ+leS39cgovXMW
-jO0tht86d0i7jzqMyUZsOQfp96FoWWHFxxONiGgYVqWUxisXHwran9N1S665vFCm
-MK3YlNK6TJKpXGkmC3W+k8Gx0+b1WfkwdUQFFrtn0IaqC/upx5BCz8tC/dEkcDSN
-CUtSsZMDQKTGhtWqnXDLvf9L9H2LNOjovvVuZPXIbly1YDy98fvhb2QcDO79mN+s
-nzBvt5OcyWnDN+zHvUpga4jL7Vh3B0ro4JVewV7rojgRl6e7n16GNtZclG3tCorP
-77gtoSu1tsvOmlFHmI2FAgwD4gPJTlzrs+8BD/9RtkXmW72V1GAR6+wEBfqe14LW
-Z1FKu80LTAMNZVgPsLqP6Sn5WEm4HEbeZnQGkHvvuHcDTe8T428KzXZoBAKkAeLV
-F83l1NBLByQEyyWl42vT5mpTwMPSvnGVMX03GNGkqkY8KTp0uZszc1tgzMY2f2sc
-qwpwEOhly0va9j4vVfDPCZZZZsIGrbZ1Q0IkB7NjpoMaumqNt8j9FFdkPN5kpItt
-7Kjz61WC7w5dIjk4mcBhos1thMpGf4Ow5m/R4QEzOlQEicN1v2nylApw9orHoYmu
-cfNoCrh9f6fxhlbqfN3nHGOv0nOJc5uHrEGIyTEpRaV+misXYiEyHKSQtgDnFGtm
-QDZHqtvOozkAD6xAQk87hxeNQPdPtIdods2U514dXy90gAl+u3Infp1iCFRmkxFH
-sRzDSplHlCJ17rM8v5kOMmQ0EwFIheB8c45FqMJ/0Z9S4SWKRcbpWWS+fbAhU2bE
-NL+/DUr8RFMXnmtrN2d9qzYIPdFBnhEBKl1H2VFp/Rnmzng8S8FUtMCGIMXVD3N0
-tTEKbyYGGKMzFVMZ6lMx3g4sCFWfxdgxqMiHgkn0oD2YJIOv4g3LCh9xDZEbV6kk
-18pXt4DxTtsjuR7XS7jv8NQlldm41KkyqhdU7pnmF1mAaKi71X1mi2HqifV0TU3+
-OgbMO2qH0plCP0D0RNJtAYN6/zJ5Dv1OL1kAeVFU4K19BtG0HsPWIECEbHMrhJYL
-vwkFb9VbaYN7xFOZQ+0V6EhS2e8wwIMd03ziSdgRlfJgSfWHFCJB+jJf+FYLQUMH
-5VPlZ4QQfXAz6ZKCuQ7S24jdJSwTeJXdKCXBIA==
-=G3QX
+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
+WtNaMpCfU92nkD2ZwTp96oiLUOVbWEaGGByh
+=IR9z
 -----END PGP MESSAGE-----
diff --git a/hswaw/laserproxy/BUILD.bazel b/hswaw/laserproxy/BUILD.bazel
new file mode 100644
index 0000000..ca8bfb1
--- /dev/null
+++ b/hswaw/laserproxy/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        "locker.go",
+        "main.go",
+        "proxy.go",
+    ],
+    importpath = "code.hackerspace.pl/hscloud/hswaw/laserproxy",
+    visibility = ["//visibility:private"],
+    deps = [
+        "//go/mirko:go_default_library",
+        "//hswaw/laserproxy/tpl:go_default_library",
+        "@com_github_golang_glog//:go_default_library",
+    ],
+)
+
+go_binary(
+    name = "laserproxy",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
diff --git a/hswaw/laserproxy/README.md b/hswaw/laserproxy/README.md
new file mode 100644
index 0000000..e843b00
--- /dev/null
+++ b/hswaw/laserproxy/README.md
@@ -0,0 +1,46 @@
+hswaw ruida laser proxy
+=======================
+
+This is a layer 7 proxy to access the Warsaw Hackerspace laser from the main LAN/wifi.
+
+For more information about actually accessing the lasercutter in the space, see [the wiki entry](https://wiki.hackerspace.pl/infra:tools:lasercutter). The rest of this file will describe the software itself.
+
+Architecture
+------------
+
+The laserproxy software is a single Go binary that runs on Customs, which has access to both the Hackerspace LAN and the laser network. It proxies UDP traffic from lasercutter users to the lasercutter itself.
+
+Only one user is allowed at a time - to implement this mutual exclusion, a Locker worker manages a single (address, note) tuple that is currently allowed to proxy traffic, and to which return traffic is forwarded back. The Locker also maintians a deadline. After the deadline expires, the lock is automatically released. A user can also release their lock ahead of time.
+
+A lock is taken through a web interface by the user that wants to access the laser.
+
+When a lock is taken, the Locker will notify a Proxy worker about this address. The Proxy will then perform the UDP proxying. As traffic is proxied, the Proxy will send bump updates to the Locker to extend the lock deadline.
+
+Development setup
+_________________
+
+Something similar to this should work for you locally enough to open the web app:
+
+    bazel run //hswaw/laserproxy -- -hspki_disable -laser_network 127.0.0.1 -client_network 127.0.0.1
+
+This will show you available flags you can pass:
+
+    bazel run //hswaw/laserproxy -- -h
+
+If you've never used `bazel`, this should work for y'all macOS newbs:
+
+    brew install bazel
+    brew install postgresql
+
+_TODO(q3k): move this to the bazel codelab once it's finished_
+
+Deployment
+----------
+
+You'll need root access to customs.
+
+    bazel build --platforms=@io_bazel_rules_go//go/toolchain:openbsd_amd64 //hswaw/laserproxy
+    ssh root@customs supervisorctl stop laserproxy
+    scp bazel-bin/hswaw/laserproxy/laserproxy_/laserproxy root@customs:/var/laserproxy/laserproxy
+    ssh root@customs supervisorctl start laserproxy
+
diff --git a/hswaw/laserproxy/locker.go b/hswaw/laserproxy/locker.go
new file mode 100644
index 0000000..8080e9b
--- /dev/null
+++ b/hswaw/laserproxy/locker.go
@@ -0,0 +1,131 @@
+package main
+
+import (
+	"context"
+	"time"
+
+	"github.com/golang/glog"
+)
+
+type lockCtrl struct {
+	getCurrent *lockCtrlGetCurrent
+	take *lockCtrlTake
+	release *lockCtrlRelease
+	subscribe *lockCtrlSubscribe
+	bump *lockCtrlBump
+}
+
+type lockCtrlGetCurrent struct {
+	res chan *lockResCurrent
+}
+
+type lockCtrlTake struct {
+	note string
+	addr string
+	prev string
+	res chan bool
+}
+
+type lockCtrlRelease struct {
+	addr string
+	force bool
+	res chan struct{}
+}
+
+type lockCtrlSubscribe struct {
+	subscriber chan *lockUpdate
+}
+
+type lockCtrlBump struct {
+	addr string
+}
+
+type lockResCurrent struct {
+	note string
+	addr string
+	deadline time.Time
+}
+
+type lockUpdate struct {
+	note string
+	addr string
+}
+
+func (s *service) runLocker(ctx context.Context, ctrlC chan *lockCtrl, ownershipDuration time.Duration) {
+	glog.Infof("Locker starting...")
+
+	var curNote string
+	var curAddr string
+	var curDeadline time.Time
+	var subscribers []chan *lockUpdate
+
+	notify := func() {
+		for _, sub := range subscribers {
+			go func() {
+				sub <- &lockUpdate{
+					note: curNote,
+					addr: curAddr,
+				}
+			}()
+		}
+	}
+
+	t := time.NewTicker(5 * time.Second)
+	defer t.Stop()
+
+	for {
+		select {
+		case <-ctx.Done():
+			err := ctx.Err()
+			glog.Errorf("Locker stoppped: %v", err)
+			return
+		case <-t.C:
+			if curAddr != "" && time.Now().After(curDeadline) {
+				glog.Infof("Expiring lock")
+				curAddr = ""
+				curNote = ""
+				notify()
+			}
+		case ctrl := <-ctrlC:
+			switch {
+			case ctrl.take != nil:
+				won := false
+				if curAddr == ctrl.take.prev {
+					won = true
+					curNote = ctrl.take.note
+					curAddr = ctrl.take.addr
+					curDeadline = time.Now().Add(ownershipDuration)
+					notify()
+					glog.Infof("Lock taken by %q %q", curNote, curAddr)
+				}
+				go func() {
+					ctrl.take.res <- won
+				}()
+			case ctrl.release != nil:
+				if curAddr == ctrl.release.addr || ctrl.release.force {
+					curAddr = ""
+					curNote = ""
+					notify()
+					glog.Infof("Lock relased")
+				}
+				go func() {
+					ctrl.release.res <- struct{}{}
+				}()
+			case ctrl.getCurrent != nil:
+				go func() {
+					ctrl.getCurrent.res <- &lockResCurrent{
+						note: curNote,
+						addr: curAddr,
+						deadline: curDeadline,
+					}
+				}()
+			case ctrl.bump != nil:
+				if curAddr != "" {
+					curDeadline = time.Now().Add(ownershipDuration)
+				}
+			case ctrl.subscribe != nil:
+				subscribers = append(subscribers, ctrl.subscribe.subscriber)
+			}
+		}
+	}
+}
diff --git a/hswaw/laserproxy/main.go b/hswaw/laserproxy/main.go
new file mode 100644
index 0000000..03c2bf8
--- /dev/null
+++ b/hswaw/laserproxy/main.go
@@ -0,0 +1,212 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"html/template"
+	"net"
+	"net/http"
+	"math/rand"
+	"strings"
+	"time"
+
+	"github.com/golang/glog"
+
+	mirko "code.hackerspace.pl/hscloud/go/mirko"
+	"code.hackerspace.pl/hscloud/hswaw/laserproxy/tpl"
+)
+
+var (
+	flagLaserAddress = "10.11.0.10"
+	flagLaserNetworkAddress = "10.11.0.1"
+	flagClientNetworkAddress = "10.8.1.2"
+	flagWebAddress = "127.0.0.1:8080"
+	flagOwnershipDuration = "1h"
+
+	tplIndex = template.Must(template.New("index.html").Parse(tpl.MustAssetString("index.html")))
+)
+
+type service struct {
+	lockCtrl chan *lockCtrl
+}
+
+func main() {
+	flag.StringVar(&flagLaserAddress, "laser_address", flagLaserAddress, "Address of Ruida controller on laser network")
+	flag.StringVar(&flagLaserNetworkAddress, "laser_network", flagLaserNetworkAddress, "Address of proxy on laser network")
+	flag.StringVar(&flagClientNetworkAddress, "client_network", flagClientNetworkAddress, "Address of proxy on client network")
+	flag.StringVar(&flagWebAddress, "web_address", flagWebAddress, "Address and port to listen on for public web connections")
+	flag.StringVar(&flagOwnershipDuration, "timeout_duration", flagOwnershipDuration, "Time after which lasercutter lock will expire when not in use")
+
+	flag.Parse()
+	m := mirko.New()
+	if err := m.Listen(); err != nil {
+		glog.Exitf("Could not listen: %v", err)
+	}
+
+	lisLaser, err := net.ListenPacket("udp", fmt.Sprintf("%s:40200", flagLaserNetworkAddress))
+	if err != nil {
+		glog.Fatalf("could not listen on laser network: %v", err)
+	}
+	defer lisLaser.Close()
+
+	lisClient, err := net.ListenPacket("udp", fmt.Sprintf("%s:50200", flagClientNetworkAddress))
+	if err != nil {
+		glog.Fatalf("could not listen on client network: %v", err)
+	}
+	defer lisClient.Close()
+
+	laserIP := net.ParseIP(flagLaserAddress)
+	if laserIP == nil {
+		glog.Fatalf("could not parse laser IP address %q", flagLaserAddress)
+	}
+	laserAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:50200", laserIP.String()))
+	if err != nil {
+		glog.Fatalf("could not make laser UDP address: %v", err)
+	}
+
+	ownershipDuration, err := time.ParseDuration(flagOwnershipDuration)
+	if err != nil {
+		glog.Fatalf("could not parse timeout duration: %v", err)
+	}
+
+	ctx := m.Context()
+	s := &service{
+		lockCtrl: make(chan *lockCtrl),
+	}
+	updates := make(chan *lockUpdate)
+	go s.runProxy(ctx, updates, laserAddr, lisLaser, lisClient)
+	go s.runLocker(ctx, s.lockCtrl, ownershipDuration)
+	s.lockCtrl <- &lockCtrl{
+		subscribe: &lockCtrlSubscribe{
+			subscriber: updates,
+		},
+	}
+
+	mux := http.NewServeMux()
+	mux.HandleFunc("/", s.handlerIndex)
+	mux.HandleFunc("/take", s.handlerTake)
+	mux.HandleFunc("/release", s.handlerRelease)
+	mux.HandleFunc("/force", s.handlerForce)
+	httpSrv := &http.Server{Addr: flagWebAddress, Handler: mux}
+
+	glog.Infof("Listening for web connections on %q...", flagWebAddress)
+	go func() {
+		if err := httpSrv.ListenAndServe(); err != nil {
+			glog.Error(err)
+		}
+	}()
+
+	if err := m.Serve(); err != nil {
+		glog.Exitf("Could not run: %v", err)
+	}
+
+	glog.Info("Running.")
+	<-m.Done()
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	httpSrv.Shutdown(ctx)
+}
+
+var (
+	sampleNames = []string{
+		"enleth", "radex", "qdot", "hans acker", "lars aser", "makłowicz",
+	}
+)
+
+func remoteAddr(r *http.Request) string {
+	if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
+		return strings.Split(fwd, ":")[0]
+	}
+	return strings.Split(r.RemoteAddr, ":")[0]
+}
+
+func (s *service) handlerIndex(w http.ResponseWriter, r *http.Request) {
+	res := make(chan *lockResCurrent)
+	s.lockCtrl <- &lockCtrl{
+		getCurrent: &lockCtrlGetCurrent{
+			res: res,
+		},
+	}
+	cur := <-res
+
+	err := tplIndex.Execute(w, struct {
+		You bool
+		CurrentAddress string
+		CurrentName string
+		CurrentDeadline string
+		SampleName string
+	}{
+		You: cur.addr == remoteAddr(r),
+		CurrentAddress: cur.addr,
+		CurrentName: cur.note,
+		CurrentDeadline: fmt.Sprintf("%d minute(s)", int(cur.deadline.Sub(time.Now()).Minutes())),
+		SampleName: sampleNames[rand.Intn(len(sampleNames))],
+	})
+	if err != nil {
+		glog.Errorf("rendering template: %v", err)
+	}
+}
+
+func (s *service) handlerTake(w http.ResponseWriter, r *http.Request) {
+	if r.Method != "POST" {
+		return
+	}
+	r.ParseForm()
+	who := r.Form.Get("who")
+	prev := r.Form.Get("prev")
+	if who == "" {
+		fmt.Fprintf(w, "excuse me, who are you? please specify a name")
+		return
+	}
+
+	res := make(chan bool)
+	take := &lockCtrlTake{
+		note: who,
+		addr: remoteAddr(r),
+		prev: prev,
+		res: res,
+	}
+	s.lockCtrl <- &lockCtrl{
+		take: take,
+	}
+	won := <-res
+
+	if won {
+		http.Redirect(w, r, "/", 302)
+	} else {
+		fmt.Fprintf(w, "lock not taken")
+	}
+}
+
+func (s *service) handlerRelease(w http.ResponseWriter, r *http.Request) {
+	if r.Method != "POST" {
+		return
+	}
+	res := make(chan struct{})
+	s.lockCtrl <- &lockCtrl{
+		release: &lockCtrlRelease{
+			addr: remoteAddr(r),
+			res: res,
+		},
+	}
+	<-res
+
+	http.Redirect(w, r, "/", 302)
+}
+
+func (s *service) handlerForce(w http.ResponseWriter, r *http.Request) {
+	if r.Method != "POST" {
+		return
+	}
+	res := make(chan struct{})
+	s.lockCtrl <- &lockCtrl{
+		release: &lockCtrlRelease{
+			force: true,
+			res: res,
+		},
+	}
+	<-res
+
+	http.Redirect(w, r, "/", 302)
+}
diff --git a/hswaw/laserproxy/proxy.go b/hswaw/laserproxy/proxy.go
new file mode 100644
index 0000000..21f9399
--- /dev/null
+++ b/hswaw/laserproxy/proxy.go
@@ -0,0 +1,69 @@
+package main
+
+import (
+	"context"
+	"net"
+	"strings"
+
+	"github.com/golang/glog"
+)
+
+type packetFrom struct {
+	addr net.Addr
+	data []byte
+}
+
+func (s *service) runProxy(ctx context.Context, updates chan *lockUpdate, laserAddr net.Addr, laserNet, clientNet net.PacketConn) {
+	glog.Infof("Proxy starting... (laser: %v, laser network: %v, client network: %v)", laserAddr, laserNet, clientNet)
+
+
+	pipe := func(conn net.PacketConn, C chan *packetFrom) {
+		for {
+			buf := make([]byte, 1500)
+			n, addr, err := conn.ReadFrom(buf)
+			if err != nil {
+				glog.Errorf("pipe failed: %v", err)
+			}
+			C <- &packetFrom{ addr, buf[:n] }
+		}
+	}
+
+	laserC := make(chan *packetFrom)
+	go pipe(laserNet, laserC)
+	clientC := make(chan *packetFrom)
+	go pipe(clientNet, clientC)
+
+	var allowedClient string
+	var curClient *net.Addr
+	for {
+		select {
+		case <-ctx.Done():
+			err := ctx.Err()
+			glog.Errorf("Proxy stopped: %v", err)
+			return
+		case u := <-updates:
+			allowedClient = u.addr
+			glog.Infof("New allowed client: %q", allowedClient)
+		case p := <-laserC:
+			s.lockCtrl <- &lockCtrl{
+				bump: &lockCtrlBump{},
+			}
+			if curClient == nil {
+				glog.Warningf("Packet from laser without client connected, dropping.")
+				break
+			}
+			clientNet.WriteTo(p.data, *curClient)
+		case p := <-clientC:
+			s.lockCtrl <- &lockCtrl{
+				bump: &lockCtrlBump{},
+			}
+			if strings.Split(p.addr.String(), ":")[0] == allowedClient {
+				curClient = &p.addr
+				laserNet.WriteTo(p.data, laserAddr)
+			} else {
+				glog.Infof("Rejecting packet from %s", p.addr.String())
+			}
+		}
+	}
+}
+
diff --git a/hswaw/laserproxy/tpl/BUILD.bazel b/hswaw/laserproxy/tpl/BUILD.bazel
new file mode 100644
index 0000000..10e3740
--- /dev/null
+++ b/hswaw/laserproxy/tpl/BUILD.bazel
@@ -0,0 +1,18 @@
+load("@io_bazel_rules_go//extras:bindata.bzl", "bindata")
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+bindata(
+    name = "tpl_bindata",
+    srcs = glob(["*"]),
+    extra_args = ["."],
+    package = "tpl",
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        ":tpl_bindata",  # keep
+    ],
+    importpath = "code.hackerspace.pl/hscloud/hswaw/laserproxy/tpl",  # keep
+    visibility = ["//hswaw/laserproxy:__subpackages__"],
+)
diff --git a/hswaw/laserproxy/tpl/index.html b/hswaw/laserproxy/tpl/index.html
new file mode 100644
index 0000000..c9fab16
--- /dev/null
+++ b/hswaw/laserproxy/tpl/index.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>HSWAW Lasercutter</title>
+    </head>
+    <body>
+        <h1>Warsaw Hackerspace SokóÅ‚ Lasercutter Proxy</h1>
+        {{ if eq .CurrentAddress "" }}
+        <p>
+            Currently <b>not</b> in use by anyone. Wanna lase something?
+        </p>
+        <form action="/take" method="POST">
+            I am <input id="persist" type="text" name="who" placeholder="{{ .SampleName }}" /> and want to <input type="submit" value="use the laser" /> over the network.
+        </form>
+        {{ else if .You }}
+        <p>
+            Currently in use by <b>you</b> ({{ .CurrentName }}, {{ .CurrentAddress }}). <b>Expires in {{ .CurrentDeadline }}.</b> This deadline will automatically extend as long as the laser is actively used.
+        </p>
+        <p>
+            To cut something, use LightBurn, and point it as <b>10.8.1.2</b> (as a 'Ruida' Ethernet/LAN controller).
+        </p>
+        <form action="/release" method="POST">
+            <input type="submit" value="I'm done with the laser." />
+        </form>
+        {{ else }}
+        <p>
+            Currently in use by '{{ .CurrentName }}' ({{ .CurrentAddress }}). <b>Expires automatically in {{ .CurrentDeadline }}</b>.
+        </p>
+        <form action="/force" method="POST">
+            I want to use the laser now, and I'm sure {{ .CurrentName }} is not using it. <input type="submit" value="I want to forcefully release the lock" />, and am aware that doing this will bring wrath onto me if someone <em>is</em> using the laser.
+        </form>
+        {{ end }}
+        <p>
+            <b>Confused by this?</b> See our <a href="https://wiki.hackerspace.pl/infra:tools:lasercutter">wiki entry about how to use the laser</a>.
+        </p>
+        <p><small><a href="https://cs.hackerspace.pl/hscloud/-/tree/hswaw/laserproxy">source code</a></small></p>
+
+        <script>
+let element = document.querySelector("#persist");
+if (element !== null) {
+    let existing = localStorage.getItem("hacker");
+    if (existing !== "" && existing !== null) {
+        element.value = existing;
+    }
+    element.addEventListener('change', (event) => {
+        let value = event.target.value;
+        localStorage.setItem("hacker", value);
+    });
+}
+        </script>
+    </body>
+</html>
diff --git a/hswaw/lib/flask_spaceauth/BUILD b/hswaw/lib/flask_spaceauth/BUILD
index 9060d58..4c29872 100644
--- a/hswaw/lib/flask_spaceauth/BUILD
+++ b/hswaw/lib/flask_spaceauth/BUILD
@@ -1,8 +1,12 @@
+load("@pydeps//:requirements.bzl", "requirement")
+
 py_binary(
     name = "example",
     srcs = ["example.py"],
     deps = [
         "//hswaw/lib/flask_spaceauth/spaceauth",
-        "@pydeps//flask",
+        requirement("flask"),
+        requirement("werkzeug"),
+        requirement("itsdangerous"),
     ]
 )
diff --git a/hswaw/lib/flask_spaceauth/spaceauth/BUILD b/hswaw/lib/flask_spaceauth/spaceauth/BUILD
index 94c1591..ee3d670 100644
--- a/hswaw/lib/flask_spaceauth/spaceauth/BUILD
+++ b/hswaw/lib/flask_spaceauth/spaceauth/BUILD
@@ -1,3 +1,5 @@
+load("@pydeps//:requirements.bzl", "requirement")
+
 py_library(
     name = "spaceauth",
     srcs = [
@@ -6,8 +8,8 @@
     ],
     visibility = ["//visibility:public"],
     deps = [
-        "@pydeps//blinker",
-        "@pydeps//flask_login",
-        "@pydeps//flask_oauthlib",
+        requirement("blinker"),
+        requirement("flask_login"),
+        requirement("flask_oauthlib"),
     ],
 )
diff --git a/kube/OWNERS b/kube/OWNERS
new file mode 100644
index 0000000..4960455
--- /dev/null
+++ b/kube/OWNERS
@@ -0,0 +1,3 @@
+inherited: false
+owners:
+  - q3k
diff --git a/kube/kube.libsonnet b/kube/kube.libsonnet
index 202b41b..929c6f2 100644
--- a/kube/kube.libsonnet
+++ b/kube/kube.libsonnet
@@ -12,6 +12,20 @@
     Certificate(name): kube._Object("certmanager.k8s.io/v1alpha1", "Certificate", name) {
         spec: error "spec must be defined",
     },
+    # For use in PodSpec.volumes_
+    CertificateVolume(certificate): {
+        secret: { secretName: certificate.spec.secretName },
+    },
+
+    # Add .Contain method to Namespaces, allowing for easy marking of particular
+    # kube objects as contained in that namespace.
+    Namespace(name): kube.Namespace(name) {
+        Contain(o):: o {
+            metadata+: {
+                namespace: name,
+            },
+        },
+    },
 
     CephObjectStoreUser(name): kube._Object("ceph.rook.io/v1", "CephObjectStoreUser", name) {
         local user = self,
@@ -24,4 +38,80 @@
         // secure way.
         secret_name:: "rook-ceph-object-user-%s-%s" % [user.spec.store, user.spec.displayName],
     },
+
+    // Make OpenAPI v3 schema specification less painful.
+    OpenAPI:: {
+        Validation(obj):: {
+            openAPIV3Schema: obj.render,
+        },
+
+        Required(inner):: inner {
+            required:: true,
+        },
+
+        Dict:: {
+            local dict = self,
+            required:: false,
+
+            local requiredList = [
+                k for k in std.filter(function(k) dict[k].required, std.objectFields(dict))
+            ],
+
+            render:: {
+                properties: {
+                    [k]: dict[k].render
+                    for k in std.objectFields(dict)
+                },
+            } + (if std.length(requiredList) > 0 then {
+                required: requiredList,
+            } else {}),
+        },
+
+        Array(items):: {
+            required:: false,
+            render:: {
+                type: "array",
+                items: items.render,
+            },
+        },
+
+        Integer:: {
+            local integer = self,
+            required:: false,
+            render:: {
+                type: "integer",
+            } + (if integer.minimum != null then {
+                minimum: integer.minimum,
+            } else {}) + (if integer.maximum != null then {
+                maximum: integer.maximum,
+            } else {}),
+
+            minimum:: null,
+            maximum:: null,
+        },
+
+        String:: {
+            local string = self,
+            required:: false,
+            render:: {
+                type: "string",
+            } + (if string.pattern != null then {
+                pattern: string.pattern,
+            } else {}),
+
+            pattern:: null,
+        },
+
+        Boolean:: {
+            required:: false,
+            render:: {
+                type: "boolean",
+            },
+        },
+
+        Any:: {
+            required:: false,
+            render:: {},
+        },
+    },
 }
diff --git a/kube/mirko.libsonnet b/kube/mirko.libsonnet
index b35833f..5203afd 100644
--- a/kube/mirko.libsonnet
+++ b/kube/mirko.libsonnet
@@ -31,6 +31,12 @@
                     service: component.svc,
                     port: component.cfg.ports.publicHTTP[p].port,
                     dns: component.cfg.ports.publicHTTP[p].dns,
+                    // Extra headers to set.
+                    // BUG(q3k): these headers are applied to all components in the environment!
+                    // We should be splitting up ingresses where necessary to combat this.
+                    setHeaders: [],
+                    // Extra paths to add to ingress. These are bare HTTPIngressPaths.
+                    extraPaths: component.cfg.extraPaths,
                 }
                 for p in std.objectFields(env.components[c].cfg.ports.publicHTTP)
             ]
@@ -50,6 +56,9 @@
                 annotations+: {
                     "kubernetes.io/tls-acme": "true",
                     "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
+                    [if env.ingressServerSnippet != null then "nginx.ingress.kubernetes.io/server-snippet"]: env.ingressServerSnippet,
+                    [if std.length(env.extraHeaders) > 0 then "nginx.ingress.kubernetes.io/configuration-snippet"]:
+                        std.join("\n", ["proxy_set_header %s;" % [h] for h in env.extraHeaders]),
                 },
             },
             spec+: {
@@ -65,13 +74,27 @@
                         http: {
                             paths: [
                                 { path: "/", backend: { serviceName: p.service.metadata.name, servicePort: p.port }},
-                            ],
+                            ] + p.extraPaths,
                         },
                     }
                     for p in env.publicHTTPPorts
                 ],
             },
-        } else {}
+        } else {},
+
+        // Nginx Ingress Controller server configuration snippet to add.
+        ingressServerSnippet:: null,
+
+        // Extra request headers to add to ingress
+        extraHeaders:: std.flattenArrays([
+            std.flattenArrays([
+
+                local portc = env.components[c].cfg.ports.publicHTTP[p];
+                if std.objectHas(portc, "setHeaders") then portc.setHeaders else []
+                for p in std.objectFields(env.components[c].cfg.ports.publicHTTP)
+            ])
+            for c in std.objectFields(env.components)
+        ]),
     },
 
     Component(env, name): {
@@ -106,11 +129,12 @@
             nodeSelector: null,
             securityContext: {},
             container:: error "container(s) must be set",
+            initContainer:: null,
             ports:: {
                 publicHTTP: {}, // name -> { port: no, dns: fqdn }
                 grpc: { main: 4200 }, // name -> port no
             },
-
+            extraPaths:: [],
         },
 
         allPorts:: {
@@ -166,6 +190,7 @@
                             },
                         } + cfg.volumes,
                         containers_: cfg.containers,
+                        [if cfg.initContainer != null then "initContainers"]: [cfg.initContainer],
                         nodeSelector: cfg.nodeSelector,
 
                         serviceAccountName: component.sa.metadata.name,
diff --git a/kube/policies.libsonnet b/kube/policies.libsonnet
index 18b5c27..0f8d3d8 100644
--- a/kube/policies.libsonnet
+++ b/kube/policies.libsonnet
@@ -5,8 +5,18 @@
 
     policyNameAllowInsecure: "policy:allow-insecure",
     policyNameAllowSecure: "policy:allow-secure",
+    policyNameAllowMostlySecure: "policy:allow-mostlysecure",
+
+    # egrep 'define CAP_[A-Z_]+.+[0-9]+$' include/linux/capability.h | cut -d' ' -f 2 | tr '\n' ','
+    local allCapsStr = 'CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_DAC_READ_SEARCH,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_SETGID,CAP_SETUID,CAP_SETPCAP,CAP_LINUX_IMMUTABLE,CAP_NET_BIND_SERVICE,CAP_NET_BROADCAST,CAP_NET_ADMIN,CAP_NET_RAW,CAP_IPC_LOCK,CAP_IPC_OWNER,CAP_SYS_MODULE,CAP_SYS_RAWIO,CAP_SYS_CHROOT,CAP_SYS_PTRACE,CAP_SYS_PACCT,CAP_SYS_ADMIN,CAP_SYS_BOOT,CAP_SYS_NICE,CAP_SYS_RESOURCE,CAP_SYS_TIME,CAP_SYS_TTY_CONFIG,CAP_MKNOD,CAP_LEASE,CAP_AUDIT_WRITE,CAP_AUDIT_CONTROL,CAP_SETFCAP,CAP_MAC_OVERRIDE,CAP_MAC_ADMIN,CAP_SYSLOG,CAP_WAKE_ALARM,CAP_BLOCK_SUSPEND,CAP_AUDIT_READ',
+    // Split by `,`, remove CAP_ prefix, turn into unique set.
+    local allCaps = std.set(std.map(function(el) std.substr(el, 4, std.length(el)-4), std.split(allCapsStr, ','))),
+
 
     Cluster: {
+        local cluster = self,
+
+        // Insecure: allowing creation of these pods allows you to pwn the entire cluster.
         insecure: kube._Object("policy/v1beta1", "PodSecurityPolicy", "insecure") {
             spec: {
                 privileged: true,
@@ -43,6 +53,9 @@
                 }
             ],
         },
+
+        // Secure: very limited subset of security policy, everyone is allowed
+        // to spawn containers of this kind.
         secure: kube._Object("policy/v1beta1", "PodSecurityPolicy", "secure") {
             spec: {
                 privileged: false,
@@ -91,6 +104,7 @@
                     ],
                 },
                 readOnlyRootFilesystem: false,
+
             },
         },
         secureRole: kube.ClusterRole(policies.policyNameAllowSecure) {
@@ -103,6 +117,51 @@
                 },
             ],
         },
+
+        // MostlySecure: like secure, but allows for setuid inside containers
+        // and enough filesystem access to run apt.
+        mostlySecure: cluster.secure {
+            metadata+: {
+                name: "mostlysecure",
+            },
+            spec+: {
+                requiredDropCapabilities: std.setDiff(allCaps, [
+                    // Drop everything apart from:
+                    "CHOWN",
+                    "DAC_OVERRIDE",
+                    "FOWNER",
+                    "LEASE",
+                    "SETGID",
+                    "SETUID",
+                ]),
+                supplementalGroups: {
+                    // Allow running as root gid - we allow running as root
+                    // uid anyway, as we trust our container runtime.
+                    rule: 'MustRunAs',
+                    ranges: [
+                        { min: 0, max: 65535, },
+                    ],
+                },
+                fsGroup: {
+                    // Allow setting the fsGroup to 0, as all filesystem mounts
+                    // are trusted anyway.
+                    rule: 'MustRunAs',
+                    ranges: [
+                        { min: 0, max: 65535, },
+                    ],
+                },
+            },
+        },
+        mostlySecureRole: kube.ClusterRole(policies.policyNameAllowMostlySecure) {
+            rules: [
+                {
+                    apiGroups: ['policy'],
+                    resources: ['podsecuritypolicies'],
+                    verbs: ['use'],
+                    resourceNames: ['mostlysecure'],
+                },
+            ],
+        },
     },
 
     # Allow insecure access to all service accounts in a given namespace.
@@ -121,4 +180,21 @@
             ],
         },
     },
+
+    # Allow mostlysecure access to all service accounts in a given namespace.
+    AllowNamespaceMostlySecure(namespace): {
+        rb: kube.RoleBinding("policy:allow-mostlysecure-in-" + namespace) {
+            metadata+: {
+                namespace: namespace,
+            },
+            roleRef_: policies.Cluster.mostlySecureRole,
+            subjects: [
+                {
+                    kind: "Group",
+                    apiGroup: "rbac.authorization.k8s.io",
+                    name: "system:serviceaccounts",
+                }
+            ],
+        },
+    },
 }
diff --git a/kube/postgres.libsonnet b/kube/postgres.libsonnet
index 8208662..e89e9db 100644
--- a/kube/postgres.libsonnet
+++ b/kube/postgres.libsonnet
@@ -16,6 +16,8 @@
         username: error "username must be set",
         # not literal, instead ref for env (like { secretKeyRef: ... })
         password: error "password must be set",
+
+        storageSize: "30Gi",
     },
 
     makeName(suffix):: cfg.prefix + suffix,
@@ -36,7 +38,7 @@
             accessModes: [ "ReadWriteOnce" ],
             resources: {
                 requests: {
-                    storage: "30Gi",
+                    storage: cfg.storageSize,
                 },
             },
         },
@@ -74,6 +76,7 @@
             },
         },
     },
+
     svc: kube.Service(postgres.makeName("postgres")) {
         metadata+: postgres.metadata,
         target_pod:: postgres.deployment.spec.template,
diff --git a/nix/readtree.nix b/nix/readtree.nix
new file mode 100644
index 0000000..066d326
--- /dev/null
+++ b/nix/readtree.nix
@@ -0,0 +1,96 @@
+# The MIT License (MIT)
+# 
+# Copyright (c) 2019 Vincent Ambo
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+{ ... }:
+
+args: initPath:
+
+let
+  inherit (builtins)
+    attrNames
+    baseNameOf
+    filter
+    hasAttr
+    head
+    isAttrs
+    length
+    listToAttrs
+    map
+    match
+    readDir
+    substring;
+
+  argsWithPath = parts:
+    let meta.locatedAt = parts;
+    in meta // (if isAttrs args then args else args meta);
+
+  readDirVisible = path:
+    let
+      children = readDir path;
+      isVisible = f: f == ".skip-subtree" || (substring 0 1 f) != ".";
+      names = filter isVisible (attrNames children);
+    in listToAttrs (map (name: {
+      inherit name;
+      value = children.${name};
+    }) names);
+
+  # The marker is added to every set that was imported directly by
+  # readTree.
+  importWithMark = path: parts:
+    let imported = import path (argsWithPath parts);
+    in if (isAttrs imported)
+      then imported // { __readTree = true; }
+      else imported;
+
+  nixFileName = file:
+    let res = match "(.*)\.nix" file;
+    in if res == null then null else head res;
+
+  readTree = path: parts:
+    let
+      dir = readDirVisible path;
+      self = importWithMark path parts;
+      joinChild = c: path + ("/" + c);
+
+      # Import subdirectories of the current one, unless the special
+      # `.skip-subtree` file exists which makes readTree ignore the
+      # children.
+      #
+      # This file can optionally contain information on why the tree
+      # should be ignored, but its content is not inspected by
+      # readTree
+      filterDir = f: dir."${f}" == "directory";
+      children = if hasAttr ".skip-subtree" dir then [] else map (c: {
+        name = c;
+        value = readTree (joinChild c) (parts ++ [ c ]);
+      }) (filter filterDir (attrNames dir));
+
+      # Import Nix files
+      nixFiles = filter (f: f != null) (map nixFileName (attrNames dir));
+      nixChildren = map (c: let p = joinChild (c + ".nix"); in {
+        name = c;
+        value = importWithMark p (parts ++ [ c ]);
+      }) nixFiles;
+    in if dir ? "default.nix"
+      then (if isAttrs self then self // (listToAttrs children) else self)
+      else listToAttrs (nixChildren ++ children);
+in readTree initPath [ (baseNameOf initPath) ]
diff --git a/ops/machines.nix b/ops/machines.nix
new file mode 100644
index 0000000..c341ec4
--- /dev/null
+++ b/ops/machines.nix
@@ -0,0 +1,53 @@
+# Top-level file aggregating all machines managed from hscloud.
+#
+# This allows to have a common attrset of machines that can be deployed
+# in the same way.
+#
+# Currently building/deployment is still done in a half-assed way:
+#
+#    machine=edge01.waw.bgp.wtf
+#    nix-build -A 'ops.machines."'$machine'"'.toplevel
+#
+# This spits out a derivation path that correponds to the built config of that
+# machine. To deploy it:
+#
+#    d=/nix/store/nkdfoobarbazl0ybhazkmeyaylmaoqcr-nixos-system-edge01-20.09pre-git
+#    nix-copy-closure --to root@$machine $d
+#    ssh root@$machine $d/bin/switch-to-configuration
+#
+# TODO(q3k): merge this with //cluster/clustercfg - this should be unified!
+
+{ hscloud, pkgs, ... }:
+
+let
+  # Stopgap measure to import //cluster/nix machine definitions into new
+  # //ops/machines infrastructure.
+  # TODO(q3k): inject defs-cluster-k0.nix / defs-machines.nix content via
+  # nixos options instead of having module definitions loading it themselves,
+  # deduplicate list of machines below with defs-machines.nix somehow.
+  mkClusterMachine = name: pkgs.nixos ({ config, pkgs, ... }: {
+    # The hostname is used by //cluster/nix machinery to load the appropriate
+    # config from defs-machines into defs-cluster-k0.
+    networking.hostName = name;
+    imports = [
+      ../cluster/nix/modules/base.nix
+      ../cluster/nix/modules/kubernetes.nix
+    ];
+  });
+
+  mkMachine = paths: pkgs.nixos ({ config, pkgs, ... }: {
+    imports = paths;
+  });
+
+in {
+  "bc01n01.hswaw.net" = mkClusterMachine "bc01n01";
+  "bc01n02.hswaw.net" = mkClusterMachine "bc01n02";
+  "bc01n03.hswaw.net" = mkClusterMachine "bc01n03";
+  "dcr01s22.hswaw.net" = mkClusterMachine "dcr01s22";
+  "dcr01s24.hswaw.net" = mkClusterMachine "dcr01s24";
+
+  "edge01.waw.bgp.wtf" = mkMachine [
+    ../bgpwtf/machines/edge01.waw.bgp.wtf.nix
+    ../bgpwtf/machines/edge01.waw.bgp.wtf-hardware.nix
+  ];
+}
diff --git a/ops/monitoring/OWNERS b/ops/monitoring/OWNERS
new file mode 100644
index 0000000..318c819
--- /dev/null
+++ b/ops/monitoring/OWNERS
@@ -0,0 +1,3 @@
+owners:
+- q3k
+- implr
diff --git a/ops/monitoring/README.md b/ops/monitoring/README.md
new file mode 100644
index 0000000..0594678
--- /dev/null
+++ b/ops/monitoring/README.md
@@ -0,0 +1,41 @@
+hscloud monitoring
+==================
+
+Quick links
+-----------
+
+ - *Old Global Dashboard*: [monitoring.hackerspace.pl](https://monitoring.hackerspace.pl) - old monitoring system, unrelated to this one, configured using Chef at management.hackerspace.pl (long since dead). This setup is supposed to replace it.
+
+Architecture
+------------
+
+The hscloud monitoring solution is two-tiered:
+
+ - at the *global* tier we run metrics aggregation, long-term storage, dashboard and alerting.
+ - at the *agent* tier we collect metrics from various sources (possibly even lower tiered agents).
+
+All agent-tier agents send metrics to all global instances.
+
+
+          .--------.     .--------.              '.
+          | global |     | global |               > - global tier
+          '--------'     '--------'              .'   (contains 'global instances')
+            |    '---. .---'    |
+            |         X         |
+            |    .---' '---.    |
+            |    |         |    |
+    .--------------.     .--------------------. '.
+    |   cluster    |     |    hswaw-proxy     |  |
+    | k0.hswaw.net |     | waw.hackerspace.pl |   > - agent tier
+    '--------------'     '--------------------' .'    (contains 'agents')
+
+
+Agent - cluster
+---------------
+
+Cluster agents are responsible from collecting Kubernetes cluster metrics. They run a prometheus server that scrapes kubelet/cadvisor/... metrics and send them off to global instances.
+
+Global Instances
+----------------
+
+Global agents run Victoria Metrics, ingest metrics from all agents, and perform long-term storage. In the future they will also run Grafana and AlertManager.
diff --git a/ops/monitoring/doc/index.md b/ops/monitoring/doc/index.md
new file mode 100644
index 0000000..b17287c
--- /dev/null
+++ b/ops/monitoring/doc/index.md
@@ -0,0 +1,38 @@
+Monitoring
+==========
+
+Setting up monitoring in hscloud is a work in progress.
+
+Components
+----------
+
+Currently we have a per-cluster setup with prometheus scraping Kubernetes nodes
+(kubeletes) for kubelet metrics and cAdvisor metrics.
+
+    .-----------------------------------------------------------.
+    |                        k0.hswaw.net                       |
+    |-----------------------------------------------------------|
+    |  .---------------------.                                  |
+    |  | ns: metrics-cluster |    .--------------------------.  |
+    |  |---------------------|    |  kubernetes.svc.cluster  |  |
+    |  | prometheus          |--> | apiserver proxy to nodes |  |
+    |  '---------------------'    '--------------------------'  |
+    |                                         |                 |
+    '---------------------------------------- v ----------------'
+                                  .---------------------.
+                                  |   bc0n01.hswaw.net  |-.
+                      Kubernetes  |---------------------| |-.
+                           Nodes  |  /metrics           |-| |
+                                  |  /metrics/cadvisor  | |-|
+                                  '---------------------' | |
+                                    '---------------------' |
+                                      '---------------------'
+
+Everything else (dashboard, aggregation, user metrics) is a work in progress.
+
+Legacy
+------
+
+There is a legacy prometheus/grafana VM on https://metrics.hackerspace.pl/. The
+certificate is expired, but it Generally Works, and will be kept going until
+its functionality is migrated to hscloud.
diff --git a/ops/monitoring/k0.jsonnet b/ops/monitoring/k0.jsonnet
new file mode 100644
index 0000000..62810c5
--- /dev/null
+++ b/ops/monitoring/k0.jsonnet
@@ -0,0 +1,39 @@
+local cluster = import "lib/cluster.libsonnet";
+local global = import "lib/global.libsonnet";
+
+// Monitoring tiers set up on k0. See README for architectural background.
+
+{
+    local k0 = self,
+    local cfg = {
+        storageClasses+: {
+            prometheus: "waw-hdd-redundant-3",
+            victoria: "waw-hdd-redundant-3",
+        },
+    },
+
+    // Cluster tier - prometheus.
+    cluster: cluster.Cluster("k0") {
+        cfg+: cfg {
+            username: "cluster-k0",
+            upstreams: [
+                { password: std.split(importstr "secrets/plain/global-agent-cluster-k0", "\n")[0], remote: k0.global.internalIngestURL  },
+            ],
+        },
+    },
+
+    // Global tier - victoria metrics.
+    global: global.Global("k0") {
+        cfg+: cfg {
+            hosts: {
+                globalAPI: "monitoring-global-api.k0.hswaw.net",
+            },
+            agents: [
+                // Ingestion from k0 cluster tier.
+                { username: k0.cluster.cfg.username, password: std.split(importstr "secrets/plain/global-agent-cluster-k0", "\n")[0], },
+                // Access from q3k's test Grafana.
+                { username: "grafana", password: std.split(importstr "secrets/plain/global-agent-grafana", "\n")[0], },
+            ],
+        }, 
+    },
+}
diff --git a/ops/monitoring/lib/cluster.libsonnet b/ops/monitoring/lib/cluster.libsonnet
new file mode 100644
index 0000000..511d426
--- /dev/null
+++ b/ops/monitoring/lib/cluster.libsonnet
@@ -0,0 +1,249 @@
+local kube = import "../../../kube/kube.libsonnet";
+
+{
+    // Cluster sets up all cluster-specific monitoring resources in their own namespace.
+    //
+    // Currently this consists of a prometheus server that scrapes k8s nodes for kubelet
+    // and cAdvisor metrics, and possibly ships over metrics to the global tier via set
+    // upstreams.
+    Cluster(name):: {
+        local cluster = self,
+        local cfg = cluster.cfg,
+        cfg:: {
+            name: name,
+            namespace: "monitoring-cluster",
+
+            images: {
+                prometheus: "prom/prometheus:v2.18.1",
+            },
+
+            storageClasses: {
+                prometheus: error "storageClasses.prometheus must be set",
+            },
+
+            // Username used to authenticate to upstreams.
+            username: error "username must be set",
+
+            // Global tier upstreams that this cluster should ship metrics off to.
+            // List of
+            //  {
+            //     remote: URL of upstream
+            //     password: password used to authenticate, in conjunction with cfg.username.
+            //  
+            upstreams: [],
+        },
+
+        namespace: kube.Namespace(cfg.namespace),
+
+        prometheus: {
+            local prometheus = self,
+
+            // Configuration that's going to be emitted as prometheus.yml and passed to the
+            // prometheus server for this cluster.
+            configuration:: {
+                global: {
+                    external_labels: {
+                        cluster: cluster.cfg.name,
+                    },
+                },
+
+                // Constructor for a Kubernetes scrape job that uses the pod's service account and
+                // TLS configuration, selecting the given k8s scrape 'role'.
+                local kubeScrapeConfig = function(name, role) {
+                    job_name: name,
+                    scheme: "https",
+                    scrape_interval: "30s",
+                    kubernetes_sd_configs: [ { role: role }, ],
+                    tls_config: {
+                        ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
+                    },
+                    bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token",
+                },
+
+                scrape_configs: [
+                    // When scraping node-based metrics (ie. node and cadvisor metrics) we contact
+                    // the metrics endpoints on the kubelet via the API server. This is done by
+                    // relabeling _address__ and __metrics_path__ to point at the k8s API server,
+                    // and at the API server proxy path to reach a node's metrics endpoint.
+                    //
+                    // This approach was lifted from the prometheus examples for Kubernetes, and
+                    // while the benefits outlined there do not matter that much to us (our
+                    // kubelets listen on public addresses, anyway), we still enjoy this approach
+                    // for the fact that we don't have to hardcode the kubelet TLS port.
+                    //
+                    // https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml
+                    //
+                    // When contacting the API server, we hardcode the 'hswaw.net' DNS suffix as
+                    // our API server's TLS certificate only has a CN/SAN for its full FQDN, not
+                    // the .svc.cluster.local shorthand (see //cluster/clustercfg:clustercfg.py).
+
+                    // Scrape Kubernetes node metrics via apiserver. This emites kube_node_* metrics.
+                    kubeScrapeConfig("cluster_node_metrics", "node") {
+                        relabel_configs: [
+                            {
+                                action: "labelmap",
+                                regex: "__meta_kubernetes_node_label_(.+)",
+                            },
+                            {
+                                action: "replace",
+                                target_label: "__address__",
+                                replacement: "kubernetes.default.svc.%s.hswaw.net:443" % [cluster.cfg.name],
+                            },
+                            {
+                                target_label: "__metrics_path__",
+                                source_labels: ["__meta_kubernetes_node_name"],
+                                regex: "(.+)",
+                                replacement: "/api/v1/nodes/${1}/proxy/metrics",
+                            },
+                        ],
+                    },
+                    // Scrape Kubernetes node cadvisor metrics via apiserver. This emits container_* metrics.
+                    kubeScrapeConfig("cluster_cadvisor_metrics", "node") {
+                        relabel_configs: [
+                            {
+                                action: "labelmap",
+                                regex: "__meta_kubernetes_node_label_(.+)",
+                            },
+                            {
+                                action: "replace",
+                                target_label: "__address__",
+                                replacement: "kubernetes.default.svc.%s.hswaw.net:443" % [cluster.cfg.name],
+                            },
+                            {
+                                target_label: "__metrics_path__",
+                                source_labels: ["__meta_kubernetes_node_name"],
+                                regex: "(.+)",
+                                replacement: "/api/v1/nodes/${1}/proxy/metrics/cadvisor",
+                            },
+                        ],
+                    },
+                ],
+
+                remote_write: [
+                    {
+                        url: u.remote,
+                        basic_auth: {
+                            username: cluster.cfg.username,
+                            password: u.password,
+                        },
+                    }
+                    for u in cluster.cfg.upstreams
+                ],
+            },
+
+            configmap: kube.ConfigMap("prometheus-cluster") {
+                metadata+: {
+                    namespace: cfg.namespace,
+                },
+                data: {
+                    "prometheus.yml": std.manifestYamlDoc(prometheus.configuration),
+                },
+            },
+
+            sa: kube.ServiceAccount("prometheus-cluster") {
+                metadata+: {
+                    namespace: cfg.namespace,
+                },
+            },
+
+            cr: kube.ClusterRole("monitoring-cluster-prometheus-server-%s" % [cfg.name]) {
+                rules: [
+                    // Allow access to all metrics.
+                    { nonResourceURLs: ["/metrics"], verbs: ["get"], },
+                    // Allow to access node details for discovery.
+                    { apiGroups: [""], resources: ["nodes"], verbs: ["list", "watch", "get"], },
+                    // Allow to proxy to bare node HTTP to access per-node metrics endpoints. 
+                    { apiGroups: [""], resources: ["nodes/proxy"], verbs: ["get"], },
+                ],
+            },
+
+            crb: kube.ClusterRoleBinding("monitoring-cluster-prometheus-server-%s" % [cfg.name]) {
+                subjects_: [prometheus.sa],
+                roleRef_: prometheus.cr,
+            },
+
+            deploy: kube.Deployment("prometheus-cluster") {
+                metadata+: {
+                    namespace: cfg.namespace,
+                },
+                spec+: {
+                    template+: {
+                        spec+: {
+                            containers_: {
+                                default: kube.Container("default") {
+                                    image: cfg.images.prometheus,
+                                    command: [
+                                        "/bin/prometheus",
+                                        "--config.file=/etc/prometheus/prometheus.yml",
+                                        "--storage.tsdb.path=/prometheus",
+                                        "--storage.tsdb.retention.size=10GB",
+                                        "--web.console.libraries=/usr/share/prometheus/console_libraries",
+                                        "--web.console.templates=/usr/share/prometheus/consoles",
+                                        "--web.enable-lifecycle",
+                                    ],
+                                    resources: {
+                                        requests: {
+                                            memory: "256Mi",
+                                            cpu: "100m",
+                                        },
+                                        limits: {
+                                            memory: "1Gi",
+                                            cpu: "1",
+                                        },
+                                    },
+                                    volumeMounts_: {
+                                        data: { mountPath: "/prometheus", },
+                                        configmap: { mountPath: "/etc/prometheus", },
+                                    },
+                                },
+                            },
+                            serviceAccountName: prometheus.sa.metadata.name,
+                            tolerations: [
+                                { key: "CriticalAddonsOnly", operator: "Exists" },
+                            ],
+                            volumes_: {
+                                data: kube.PersistentVolumeClaimVolume(prometheus.pvc),
+                                configmap: kube.ConfigMapVolume(prometheus.configmap),
+                            },
+                        },
+                    },
+                },
+            },
+
+            // Kubernetes metric storage volume.
+            pvc: kube.PersistentVolumeClaim("prometheus-cluster") {
+                metadata+: {
+                    namespace: cfg.namespace,
+                },
+                spec+: {
+                    storageClassName: cfg.storageClasses.prometheus,
+                    accessModes: ["ReadWriteOnce"],
+                    resources: {
+                        requests: {
+                            storage: "16Gi",
+                        },
+                    },
+                },
+            },
+
+            // Network Policy governing access to the prometheus server.
+            np: kube.NetworkPolicy("prometheus-cluster") {
+                metadata+: {
+                    namespace: cfg.namespace,
+                },
+                spec+: kube.podLabelsSelector(prometheus.deploy) {
+                    ingress_: {
+                        // Deny all inbound traffic to pod.
+                        // This will be augmented to allow access from some other pod/namespace
+                        // in the future.
+                    },
+                    egress_: {
+                        // Allow all outbound traffic from pod.
+                        outboundAll: {},
+                    },
+                    policyTypes: ["Ingress", "Egress"],
+                },
+            },
+        },
+    },
+}
diff --git a/ops/monitoring/lib/global.libsonnet b/ops/monitoring/lib/global.libsonnet
new file mode 100644
index 0000000..dbdbebb
--- /dev/null
+++ b/ops/monitoring/lib/global.libsonnet
@@ -0,0 +1,149 @@
+local kube = import "../../../kube/kube.libsonnet";
+
+{
+    // Global sets up a global tier instance of the hscloud monitoring infrastructure.
+    //
+    // This currently consists of Victoria Metrics, to which the agent tier sends metrics data via
+    // the prometheus remote_write protocol.
+    // Victoria Metrics is here used as a long-term storage solution. However, right now, it
+    // just keeps data locally on disk. In the future, S3 snapshots/backups should be introduced.
+    Global(name):: {
+        local global = self,
+        local cfg = global.cfg,
+
+        cfg:: {
+            name: name,
+            namespace: "monitoring-global-%s" % [cfg.name],
+
+            images: {
+                victoria: "victoriametrics/victoria-metrics:v1.40.0",
+                vmauth: "victoriametrics/vmauth:v1.40.0",
+            },
+
+            hosts: {
+                // DNS hostname that this global tier will use. Ingress will run under it.
+                globalAPI: error "hosts.globalAPI must be set",
+            },
+
+            storageClasses: {
+                // Storage class used for main data retention.
+                victoria: error "storageClasses.victoria must be set",
+            },
+
+            // A list of agents that will push metrics to this instance.
+            // List of:
+            // {
+            //   username: the username that the agent will authenticate with
+            //   password: the password that the agent will authenticate with
+            // }
+            agents: [],
+        },
+
+        // Generated URLs that agents should use to ship metrics over. Both require HTTP basic
+        // auth, configured via cfg.agents.
+        // The internal URL should be used for agents colocated in the same Kubernetes cluster.
+        internalIngestURL:: "http://%s/api/v1/write" % [global.victoria.serviceAPI.host_colon_port],
+        // The glboal URL should be used for agents sending data over the internet.
+        globalIngestURL:: "https://%s/api/v1/write" % [cfg.hosts.globalAPI],
+
+        namespace: kube.Namespace(cfg.namespace),
+        local ns = global.namespace,
+
+        victoria: {
+            local victoria = self,
+
+            pvc: ns.Contain(kube.PersistentVolumeClaim("victoria-data")) {
+                spec+: {
+                    storageClassName: cfg.storageClasses.victoria,
+                    accessModes: ["ReadWriteOnce"],
+                    resources: {
+                        requests: {
+                            storage: "64Gi",
+                        },
+                    },
+                },
+            },
+
+            authSecret: ns.Contain(kube.Secret("vmauth")) {
+                data+: {
+                    "config.yaml": std.base64(std.manifestJson({
+                        users: [
+                            {
+                                username: a.username,
+                                password: a.password,
+                                url_prefix: "http://localhost:8428",
+                            }
+                            for a in cfg.agents
+                        ],
+                    }) + "\n")
+                },
+            },
+
+            deploy: ns.Contain(kube.Deployment("victoria")) {
+                spec+: {
+                    template+: {
+                        spec+: {
+                            containers_: {
+                                default: kube.Container("default") {
+                                    image: cfg.images.victoria,
+                                    volumeMounts_: {
+                                        data: { mountPath: "/victoria-metrics-data", },
+                                    },
+                                },
+                                vmauth: kube.Container("vmauth") {
+                                    image: cfg.images.vmauth,
+                                    command: [
+                                        "/vmauth-prod",
+                                        "-auth.config", "/mnt/secret/config.yaml",
+                                    ],
+                                    volumeMounts_: {
+                                        secret: { mountPath: "/mnt/secret", },
+                                    },
+                                    ports_: {
+                                        api: { containerPort: 8427 }
+                                    },
+                                }
+                            },
+                            volumes_: {
+                                data: kube.PersistentVolumeClaimVolume(victoria.pvc),
+                                secret: kube.SecretVolume(victoria.authSecret),
+                            },
+                        },
+                    },
+                },
+            },
+
+            serviceAPI: ns.Contain(kube.Service("victoria-api")) {
+                target_pod: victoria.deploy.spec.template,
+                spec+: {
+                    ports: [
+                        { name: "api", port: 8427, targetPort: 8427, protocol: "TCP" },
+                    ],
+                    type: "ClusterIP",
+                },
+            },
+
+            ingressAPI: ns.Contain(kube.Ingress("victoria-api")) {
+                metadata+: {
+                    annotations+: {
+                        "kubernetes.io/tls-acme": "true",
+                        "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
+                    },
+                },
+                spec+: {
+                    tls: [
+                        { hosts: [cfg.hosts.globalAPI], secretName: "ingress-tls" },
+                    ],
+                    rules: [
+                        {
+                            host: cfg.hosts.globalAPI,
+                            http: {
+                                paths: [ { path: "/", backend: { serviceName: victoria.serviceAPI.metadata.name, servicePort: 8427 } }, ],
+                            },
+                        }
+                    ],
+                },
+            },
+        },
+    }
+}
diff --git a/ops/monitoring/lib/grafonnet/DOCS.md b/ops/monitoring/lib/grafonnet/DOCS.md
new file mode 100644
index 0000000..0c78596
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/DOCS.md
@@ -0,0 +1,846 @@
+# Docs
+
+* [dashboard](#dashboard)
+* [panel](#panel)
+  * [gauge.new](#panelGaugenew)
+  * [graph.new](#panelGraphnew)
+  * [row.new](#panelRownew)
+  * [stat.new](#panelStatnew)
+  * [table.new](#panelTablenew)
+  * [text.new](#panelTextnew)
+* [target](#target)
+  * [prometheus.new](#targetPrometheusnew)
+* [template](#template)
+  * [datasource.new](#templateDatasourcenew)
+  * [query.new](#templateQuerynew)
+
+## dashboard
+
+
+
+### dashboard.new
+
+Instantiate a dashboard.
+
+* **description**: (type: string, default: `null`)
+  
+* **editable**: (type: boolean, default: `true`)
+  
+* **graphTooltip**: (type: integer, default: `0`)
+  
+* **refresh**: (type: string, default: `null`)
+  
+* **schemaVersion**: (type: integer, default: `25`)
+  
+* **style**: (type: string, default: `"dark"`)
+  
+* **tags**: (type: array, default: `[]`)
+  
+* **timezone**: (type: string, default: `null`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **uid**: (type: string, default: `null`)
+  
+
+#### #setTime
+
+* **from**: (type: string, default: `"now-6h"`)
+  
+* **to**: (type: string, default: `"now"`)
+  
+#### #setTimepicker
+
+* **refreshIntervals**: (type: array, default: `["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"]`)
+  
+
+#### #addAnnotation
+
+* **builtIn**: (type: integer, default: `0`)
+  
+* **datasource**: (type: string, default: `"default"`)
+  
+* **enable**: (type: boolean, default: `true`)
+  
+* **hide**: (type: boolean, default: `false`)
+  
+* **iconColor**: (type: string, default: `null`)
+  
+* **name**: (type: string, default: `null`)
+  
+* **rawQuery**: (type: string, default: `null`)
+  
+* **showIn**: (type: integer, default: `0`)
+  
+#### #addTemplate
+
+* **template**: (type: object)
+  
+
+
+## panel
+
+
+
+### panel.gauge.new
+
+
+
+* **datasource**: (type: string, default: `"default"`)
+  
+* **description**: (type: string, default: `null`)
+  
+* **repeat**: (type: string, default: `null`)
+  
+* **repeatDirection**: (type: string, default: `null`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **transparent**: (type: boolean, default: `false`)
+  
+
+#### #setFieldConfig
+
+* **max**: (type: integer, default: `null`)
+  
+* **min**: (type: integer, default: `null`)
+  
+* **thresholdMode**: (type: string, default: `"absolute"`)
+  
+* **unit**: (type: string, default: `null`)
+  
+#### #setGridPos
+
+* **h**: (type: integer, default: `8`)
+  Panel height.
+* **w**: (type: integer, default: `12`)
+  Panel width.
+* **x**: (type: integer, default: `null`)
+  Panel x position.
+* **y**: (type: integer, default: `null`)
+  Panel y position.
+#### #setOptions
+
+* **calcs**: (type: array, default: `["mean"]`)
+  
+* **fields**: (type: string, default: `null`)
+  
+* **orientation**: (type: string, default: `"auto"`)
+  
+* **showThresholdLabels**: (type: boolean, default: `false`)
+  
+* **showThresholdMarkers**: (type: boolean, default: `true`)
+  
+* **values**: (type: boolean, default: `false`)
+  
+
+#### #addPanelLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addDataLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addMapping
+
+* **from**: (type: string, default: `null`)
+  
+* **id**: (type: integer, default: `null`)
+  
+* **operator**: (type: string, default: `null`)
+  
+* **text**: (type: string, default: `null`)
+  
+* **to**: (type: string, default: `null`)
+  
+* **type**: (type: integer, default: `null`)
+  
+* **value**: (type: string, default: `null`)
+  
+#### #addOverride
+
+* **matcher**: (type: oject, default: `null`)
+  
+* **properties**: (type: array, default: `null`)
+  
+#### #addThresholdStep
+
+* **color**: (type: string, default: `null`)
+  
+* **value**: (type: integer, default: `null`)
+  
+#### #addTarget
+
+* **target**: (type: object)
+  
+
+
+### panel.graph.new
+
+
+
+* **bars**: (type: boolean, default: `false`)
+  Display values as a bar chart.
+* **dashLength**: (type: integer, default: `10`)
+  Dashed line length.
+* **dashes**: (type: boolean, default: `false`)
+  Show line with dashes.
+* **datasource**: (type: string, default: `"default"`)
+  
+* **decimals**: (type: integer, default: `null`)
+  Controls how many decimals are displayed for legend values and
+  graph hover tooltips.
+* **description**: (type: string, default: `null`)
+  
+* **fill**: (type: integer, default: `1`)
+  Amount of color fill for a series. Expects a value between 0 and 1.
+* **fillGradient**: (type: integer, default: `0`)
+  Degree of gradient on the area fill. 0 is no gradient, 10 is a
+  steep gradient.
+* **hiddenSeries**: (type: boolean, default: `false`)
+  Hide the series.
+* **lines**: (type: boolean, default: `true`)
+  Display values as a line graph.
+* **linewidth**: (type: integer, default: `1`)
+  The width of the line for a series.
+* **nullPointMode**: (type: string, default: `"null"`)
+  How null values are displayed.
+  * 'null' - If there is a gap in the series, meaning a null value,
+    then the line in the graph will be broken and show the gap.
+  * 'null as zero' - If there is a gap in the series, meaning a null
+    value, then it will be displayed as a zero value in the graph
+    panel.
+  * 'connected' - If there is a gap in the series, meaning a null
+    value or values, then the line will skip the gap and connect to the
+    next non-null value.
+* **percentage**: (type: boolean, default: `false`)
+  Available when `stack` is true. Each series is drawn as a percentage
+  of the total of all series.
+* **pointradius**: (type: integer, default: `null`)
+  Controls how large the points are.
+* **points**: (type: boolean, default: `false`)
+  Display points for values.
+* **repeat**: (type: string, default: `null`)
+  
+* **repeatDirection**: (type: string, default: `null`)
+  
+* **spaceLength**: (type: integer, default: `10`)
+  Dashed line spacing when `dashes` is true.
+* **stack**: (type: boolean, default: `false`)
+  Each series is stacked on top of another.
+* **steppedLine**: (type: boolean, default: `false`)
+  Draws adjacent points as staircase.
+* **timeFrom**: (type: string, default: `null`)
+  
+* **timeShift**: (type: string, default: `null`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **transparent**: (type: boolean, default: `false`)
+  
+
+#### #setGridPos
+
+* **h**: (type: integer, default: `8`)
+  Panel height.
+* **w**: (type: integer, default: `12`)
+  Panel width.
+* **x**: (type: integer, default: `null`)
+  Panel x position.
+* **y**: (type: integer, default: `null`)
+  Panel y position.
+#### #setLegend
+
+* **alignAsTable**: (type: boolean, default: `null`)
+  Whether to display legend in table.
+* **avg**: (type: boolean, default: `false`)
+  Average of all values returned from the metric query.
+* **current**: (type: boolean, default: `false`)
+  Last value returned from the metric query.
+* **max**: (type: boolean, default: `false`)
+  Maximum of all values returned from the metric query.
+* **min**: (type: boolean, default: `false`)
+  Minimum of all values returned from the metric query.
+* **rightSide**: (type: boolean, default: `false`)
+  Display legend to the right.
+* **show**: (type: boolean, default: `true`)
+  Show or hide the legend.
+* **sideWidth**: (type: integer, default: `null`)
+  Available when `rightSide` is true. The minimum width for the legend in
+  pixels.
+* **total**: (type: boolean, default: `false`)
+  Sum of all values returned from the metric query.
+* **values**: (type: boolean, default: `true`)
+  
+#### #setThresholds
+
+* **thresholdMode**: (type: string, default: `"absolute"`)
+  
+#### #setTooltip
+
+* **shared**: (type: boolean, default: `true`)
+  * true - The hover tooltip shows all series in the graph.
+    Grafana highlights the series that you are hovering over in
+    bold in the series list in the tooltip.
+  * false - The hover tooltip shows only a single series, the one
+    that you are hovering over on the graph.
+* **sort**: (type: integer, default: `2`)
+  * 0 (none) - The order of the series in the tooltip is
+    determined by the sort order in your query. For example, they
+    could be alphabetically sorted by series name.
+  * 1 (increasing) - The series in the hover tooltip are sorted
+    by value and in increasing order, with the lowest value at the
+    top of the list.
+  * 2 (decreasing) - The series in the hover tooltip are sorted
+    by value and in decreasing order, with the highest value at the
+    top of the list.
+#### #setXaxis
+
+* **buckets**: (type: string, default: `null`)
+  
+* **mode**: (type: string, default: `"time"`)
+  The display mode completely changes the visualization of the
+  graph panel. It’s like three panels in one. The main mode is
+  the time series mode with time on the X-axis. The other two
+  modes are a basic bar chart mode with series on the X-axis
+  instead of time and a histogram mode.
+  * 'time' - The X-axis represents time and that the data is
+    grouped by time (for example, by hour, or by minute).
+  * 'series' - The data is grouped by series and not by time. The
+    Y-axis still represents the value.
+  * 'histogram' - Converts the graph into a histogram. A histogram
+    is a kind of bar chart that groups numbers into ranges, often
+    called buckets or bins. Taller bars show that more data falls
+    in that range.
+* **name**: (type: string, default: `null`)
+  
+* **show**: (type: boolean, default: `true`)
+  Show or hide the axis.
+#### #setYaxis
+
+* **align**: (type: boolean, default: `false`)
+  Align left and right Y-axes by value.
+* **alignLevel**: (type: integer, default: `0`)
+  Available when align is true. Value to use for alignment of
+  left and right Y-axes, starting from Y=0.
+
+#### #addDataLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addPanelLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addOverride
+
+* **matcher**: (type: oject, default: `null`)
+  
+* **properties**: (type: array, default: `null`)
+  
+#### #addSeriesOverride
+
+* **alias**: (type: string, default: `null`)
+  Alias or regex matching the series you'd like to target.
+* **bars**: (type: boolean, default: `null`)
+  
+* **color**: (type: string, default: `null`)
+  
+* **dashLength**: (type: integer, default: `null`)
+  
+* **dashes**: (type: boolean, default: `null`)
+  
+* **fill**: (type: integer, default: `null`)
+  
+* **fillBelowTo**: (type: string, default: `null`)
+  
+* **fillGradient**: (type: integer, default: `null`)
+  
+* **hiddenSeries**: (type: boolean, default: `null`)
+  
+* **hideTooltip**: (type: boolean, default: `null`)
+  
+* **legend**: (type: boolean, default: `null`)
+  
+* **lines**: (type: boolean, default: `null`)
+  
+* **linewidth**: (type: integer, default: `null`)
+  
+* **nullPointMode**: (type: string, default: `null`)
+  
+* **pointradius**: (type: integer, default: `null`)
+  
+* **points**: (type: boolean, default: `null`)
+  
+* **spaceLength**: (type: integer, default: `null`)
+  
+* **stack**: (type: integer, default: `null`)
+  
+* **steppedLine**: (type: boolean, default: `null`)
+  
+* **transform**: (type: string, default: `null`)
+  
+* **yaxis**: (type: integer, default: `null`)
+  
+* **zindex**: (type: integer, default: `null`)
+  
+#### #addThresholdStep
+
+* **color**: (type: string, default: `null`)
+  
+* **value**: (type: integer, default: `null`)
+  
+#### #addTarget
+
+* **target**: (type: object)
+  
+#### #addYaxis
+
+* **decimals**: (type: integer, default: `null`)
+  Defines how many decimals are displayed for Y value.
+* **format**: (type: string, default: `"short"`)
+  The display unit for the Y value.
+* **label**: (type: string, default: `null`)
+  The Y axis label.
+* **logBase**: (type: integer, default: `1`)
+  The scale to use for the Y value - linear, or logarithmic.
+  * 1 - linear
+  * 2 - log (base 2)
+  * 10 - log (base 10)
+  * 32 - log (base 32)
+  * 1024 - log (base 1024)
+* **max**: (type: integer, default: `null`)
+  The maximum Y value.
+* **min**: (type: integer, default: `null`)
+  The minimum Y value.
+* **show**: (type: boolean, default: `true`)
+  Show or hide the axis.
+
+
+### panel.row.new
+
+
+
+* **collapse**: (type: boolean, default: `true`)
+  
+* **collapsed**: (type: boolean, default: `true`)
+  
+* **datasource**: (type: string, default: `null`)
+  
+* **repeat**: (type: string, default: `null`)
+  
+* **repeatIteration**: (type: string, default: `null`)
+  
+* **showTitle**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **titleSize**: (type: string, default: `"h6"`)
+  
+
+#### #setGridPos
+
+* **h**: (type: integer, default: `8`)
+  Panel height.
+* **w**: (type: integer, default: `12`)
+  Panel width.
+* **x**: (type: integer, default: `null`)
+  Panel x position.
+* **y**: (type: integer, default: `null`)
+  Panel y position.
+
+#### #addPanel
+
+* **panel**: (type: object)
+  
+
+
+### panel.stat.new
+
+
+
+* **datasource**: (type: string, default: `"default"`)
+  
+* **description**: (type: string, default: `null`)
+  
+* **repeat**: (type: string, default: `null`)
+  
+* **repeatDirection**: (type: string, default: `null`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **transparent**: (type: boolean, default: `false`)
+  
+
+#### #setFieldConfig
+
+* **max**: (type: integer, default: `null`)
+  
+* **min**: (type: integer, default: `null`)
+  
+* **thresholdMode**: (type: string, default: `"absolute"`)
+  
+* **unit**: (type: string, default: `null`)
+  
+#### #setGridPos
+
+* **h**: (type: integer, default: `8`)
+  Panel height.
+* **w**: (type: integer, default: `12`)
+  Panel width.
+* **x**: (type: integer, default: `null`)
+  Panel x position.
+* **y**: (type: integer, default: `null`)
+  Panel y position.
+#### #setOptions
+
+* **calcs**: (type: array, default: `["mean"]`)
+  
+* **colorMode**: (type: string, default: `"value"`)
+  
+* **fields**: (type: string, default: `null`)
+  
+* **graphMode**: (type: string, default: `"none"`)
+  
+* **justifyMode**: (type: string, default: `"auto"`)
+  
+* **orientation**: (type: string, default: `"auto"`)
+  
+* **textMode**: (type: string, default: `"auto"`)
+  
+* **values**: (type: boolean, default: `false`)
+  
+
+#### #addDataLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addPanelLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addMapping
+
+* **from**: (type: string, default: `null`)
+  
+* **id**: (type: integer, default: `null`)
+  
+* **operator**: (type: string, default: `null`)
+  
+* **text**: (type: string, default: `null`)
+  
+* **to**: (type: string, default: `null`)
+  
+* **type**: (type: integer, default: `null`)
+  
+* **value**: (type: string, default: `null`)
+  
+#### #addOverride
+
+* **matcher**: (type: oject, default: `null`)
+  
+* **properties**: (type: array, default: `null`)
+  
+#### #addThresholdStep
+
+* **color**: (type: string, default: `null`)
+  
+* **value**: (type: integer, default: `null`)
+  
+#### #addTarget
+
+* **target**: (type: object)
+  
+
+
+### panel.table.new
+
+
+
+* **datasource**: (type: string, default: `"default"`)
+  
+* **description**: (type: string, default: `null`)
+  
+* **repeat**: (type: string, default: `null`)
+  
+* **repeatDirection**: (type: string, default: `null`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **transparent**: (type: boolean, default: `false`)
+  
+
+#### #setFieldConfig
+
+* **displayName**: (type: string, default: `null`)
+  
+* **max**: (type: integer, default: `0`)
+  
+* **min**: (type: integer, default: `0`)
+  
+* **thresholdMode**: (type: string, default: `"absolute"`)
+  
+* **noValue**: (type: string, default: `null`)
+  
+* **unit**: (type: string, default: `"short"`)
+  
+* **width**: (type: integer, default: `null`)
+  
+#### #setGridPos
+
+* **h**: (type: integer, default: `8`)
+  Panel height.
+* **w**: (type: integer, default: `12`)
+  Panel width.
+* **x**: (type: integer, default: `null`)
+  Panel x position.
+* **y**: (type: integer, default: `null`)
+  Panel y position.
+#### #setOptions
+
+* **showHeader**: (type: boolean, default: `true`)
+  
+
+#### #addPanelLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addDataLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addMapping
+
+* **from**: (type: string, default: `null`)
+  
+* **id**: (type: integer, default: `null`)
+  
+* **operator**: (type: string, default: `null`)
+  
+* **text**: (type: string, default: `null`)
+  
+* **to**: (type: string, default: `null`)
+  
+* **type**: (type: integer, default: `null`)
+  
+* **value**: (type: string, default: `null`)
+  
+#### #addOverride
+
+* **matcher**: (type: oject, default: `null`)
+  
+* **properties**: (type: array, default: `null`)
+  
+#### #addThresholdStep
+
+* **color**: (type: string, default: `null`)
+  
+* **value**: (type: integer, default: `null`)
+  
+#### #addTarget
+
+* **target**: (type: object)
+  
+
+
+### panel.text.new
+
+
+
+* **content**: (type: string, default: `null`)
+  
+* **datasource**: (type: string, default: `"default"`)
+  
+* **description**: (type: string, default: `null`)
+  
+* **mode**: (type: string, default: `"markdown"`)
+  
+* **repeat**: (type: string, default: `null`)
+  
+* **repeatDirection**: (type: string, default: `null`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **transparent**: (type: boolean, default: `false`)
+  
+
+#### #setGridPos
+
+* **h**: (type: integer, default: `8`)
+  Panel height.
+* **w**: (type: integer, default: `12`)
+  Panel width.
+* **x**: (type: integer, default: `null`)
+  Panel x position.
+* **y**: (type: integer, default: `null`)
+  Panel y position.
+
+#### #addPanelLink
+
+* **targetBlank**: (type: boolean, default: `true`)
+  
+* **title**: (type: string, default: `null`)
+  
+* **url**: (type: string, default: `null`)
+  
+#### #addTarget
+
+* **target**: (type: object)
+  
+
+
+
+## target
+
+
+
+### target.prometheus.new
+
+
+
+* **datasource**: (type: string, default: `"default"`)
+  
+* **expr**: (type: string, default: `null`)
+  
+* **format**: (type: string, default: `"time_series"`)
+  
+* **interval**: (type: string, default: `null`)
+  
+* **intervalFactor**: (type: integer, default: `null`)
+  
+* **legendFormat**: (type: string, default: `null`)
+  
+
+
+
+
+
+## template
+
+
+
+### tamplate.datasource.new
+
+
+
+* **hide**: (type: integer, default: `0`)
+  
+* **includeAll**: (type: boolean, default: `false`)
+  
+* **label**: (type: string, default: `null`)
+  
+* **multi**: (type: boolean, default: `false`)
+  
+* **name**: (type: string, default: `null`)
+  
+* **query**: (type: string, default: `null`)
+  
+* **refresh**: (type: integer, default: `1`)
+  
+* **regex**: (type: string, default: `null`)
+  
+* **skipUrlSync**: (type: string, default: `false`)
+  
+
+#### #setCurrent
+
+* **selected**: (type: boolean, default: `false`)
+  
+* **text**: (type: string, default: `null`)
+  
+* **value**: (type: string, default: `null`)
+  
+
+
+
+### tamplate.query.new
+
+
+
+* **allValue**: (type: string, default: `null`)
+  
+* **datasource**: (type: string, default: `null`)
+  
+* **definition**: (type: string, default: `null`)
+  
+* **hide**: (type: integer, default: `0`)
+  
+* **includeAll**: (type: boolean, default: `false`)
+  
+* **label**: (type: string, default: `null`)
+  
+* **multi**: (type: boolean, default: `false`)
+  
+* **name**: (type: string, default: `null`)
+  
+* **query**: (type: string, default: `null`)
+  
+* **refresh**: (type: integer, default: `0`)
+  
+* **regex**: (type: string, default: `null`)
+  
+* **skipUrlSync**: (type: string, default: `false`)
+  
+* **sort**: (type: integer, default: `0`)
+  
+* **tagValuesQuery**: (type: string, default: `null`)
+  
+* **tags**: (type: array, default: `null`)
+  
+* **tagsQuery**: (type: string, default: `null`)
+  
+* **useTags**: (type: boolean, default: `false`)
+  
+
+#### #setCurrent
+
+* **selected**: (type: boolean, default: `null`)
+  
+* **text**: (type: string, default: `null`)
+  
+* **value**: (type: string, default: `null`)
+  
+
+#### #addOption
+
+* **selected**: (type: boolean, default: `true`)
+  
+* **text**: (type: string, default: `null`)
+  
+* **value**: (type: string, default: `null`)
+  
+
+
diff --git a/ops/monitoring/lib/grafonnet/README.hscloud b/ops/monitoring/lib/grafonnet/README.hscloud
new file mode 100644
index 0000000..5b86f96
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/README.hscloud
@@ -0,0 +1,7 @@
+This subpath (//ops/monitoring/lib/grafonnet) comes from the following Git repository:
+
+    repository: https://github.com/grafana/grafonnet-lib.git
+    revision: ff69572caf78c3163980d0d723c85a722eab73d9
+    subpath: grafonnet-7.0
+
+The files contained in this subdirectory are license under the Apache 2.0 License (see //third_party/licenses/Apache-2.0.txt).
diff --git a/ops/monitoring/lib/grafonnet/dashboard.libsonnet b/ops/monitoring/lib/grafonnet/dashboard.libsonnet
new file mode 100644
index 0000000..28dc13a
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/dashboard.libsonnet
@@ -0,0 +1,85 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    description=null,
+    editable=true,
+    graphTooltip=0,
+    refresh=null,
+    schemaVersion=25,
+    style='dark',
+    tags=[],
+    timezone=null,
+    title=null,
+    uid=null,
+  ):: {
+    [if description != null then 'description']: description,
+    [if editable != null then 'editable']: editable,
+    [if graphTooltip != null then 'graphTooltip']: graphTooltip,
+    [if refresh != null then 'refresh']: refresh,
+    [if schemaVersion != null then 'schemaVersion']: schemaVersion,
+    [if style != null then 'style']: style,
+    [if tags != null then 'tags']: tags,
+    [if timezone != null then 'timezone']: timezone,
+    [if title != null then 'title']: title,
+    [if uid != null then 'uid']: uid,
+
+    setTime(
+      from='now-6h',
+      to='now',
+    ):: self {}
+        + { time+: { [if from != null then 'from']: from } }
+        + { time+: { [if to != null then 'to']: to } }
+    ,
+
+    setTimepicker(
+      refreshIntervals=['5s', '10s', '30s', '1m', '5m', '15m', '30m', '1h', '2h', '1d'],
+    ):: self {}
+        + { timepicker+: { [if refreshIntervals != null then 'refresh_intervals']: refreshIntervals } }
+    ,
+
+
+    addTemplate(
+      template
+    ):: self {}
+        + { templating+: { list+: [
+          template,
+        ] } },
+
+    addAnnotation(
+      builtIn=0,
+      datasource='default',
+      enable=true,
+      hide=false,
+      iconColor=null,
+      name=null,
+      rawQuery=null,
+      showIn=0,
+    ):: self {}
+        + { annotations+: { list+: [
+          {
+            [if builtIn != null then 'builtIn']: builtIn,
+            [if datasource != null then 'datasource']: datasource,
+            [if enable != null then 'enable']: enable,
+            [if hide != null then 'hide']: hide,
+            [if iconColor != null then 'iconColor']: iconColor,
+            [if name != null then 'name']: name,
+            [if rawQuery != null then 'rawQuery']: rawQuery,
+            [if showIn != null then 'showIn']: showIn,
+          },
+        ] } },
+
+
+    panels: [],
+    _nextPanelID:: 2,
+    addPanel(panel):: self {
+      local nextPanelID = super._nextPanelID,
+      panels+: [
+        panel { id: nextPanelID } +
+        if 'panels' in panel then { panels: std.mapWithIndex(function(i, p) p { id: nextPanelID + i + 1 }, panel.panels) } else {},
+      ],
+      _nextPanelID:: nextPanelID + 1 + (if 'panels' in panel then std.length(panel.panels) else 0),
+    },
+    addPanels(panels):: std.foldl(function(d, p) d.addPanel(p), panels, self),
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/grafana.libsonnet b/ops/monitoring/lib/grafonnet/grafana.libsonnet
new file mode 100644
index 0000000..738a051
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/grafana.libsonnet
@@ -0,0 +1,20 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  dashboard:: import 'dashboard.libsonnet',
+  panel:: {
+    gauge:: import 'panel/gauge.libsonnet',
+    graph:: import 'panel/graph.libsonnet',
+    row:: import 'panel/row.libsonnet',
+    stat:: import 'panel/stat.libsonnet',
+    table:: import 'panel/table.libsonnet',
+    text:: import 'panel/text.libsonnet',
+  },
+  target:: {
+    prometheus:: import 'target/prometheus.libsonnet',
+  },
+  template:: {
+    datasource:: import 'template/datasource.libsonnet',
+    query:: import 'template/query.libsonnet',
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/panel/gauge.libsonnet b/ops/monitoring/lib/grafonnet/panel/gauge.libsonnet
new file mode 100644
index 0000000..4520444
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/panel/gauge.libsonnet
@@ -0,0 +1,138 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    datasource='default',
+    description=null,
+    repeat=null,
+    repeatDirection=null,
+    title=null,
+    transparent=false,
+  ):: {
+    [if datasource != null then 'datasource']: datasource,
+    [if description != null then 'description']: description,
+    [if repeat != null then 'repeat']: repeat,
+    [if repeatDirection != null then 'repeatDirection']: repeatDirection,
+    [if title != null then 'title']: title,
+    [if transparent != null then 'transparent']: transparent,
+    type: 'gauge',
+
+    setFieldConfig(
+      max=null,
+      min=null,
+      thresholdMode='absolute',
+      unit=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { [if max != null then 'max']: max } } }
+        + { fieldConfig+: { defaults+: { [if min != null then 'min']: min } } }
+        + { fieldConfig+: { defaults+: { thresholds+: { [if thresholdMode != null then 'mode']: thresholdMode } } } }
+        + { fieldConfig+: { defaults+: { [if unit != null then 'unit']: unit } } }
+    ,
+
+    setGridPos(
+      h=8,
+      w=12,
+      x=null,
+      y=null,
+    ):: self {}
+        + { gridPos+: { [if h != null then 'h']: h } }
+        + { gridPos+: { [if w != null then 'w']: w } }
+        + { gridPos+: { [if x != null then 'x']: x } }
+        + { gridPos+: { [if y != null then 'y']: y } }
+    ,
+
+    setOptions(
+      calcs=['mean'],
+      fields=null,
+      orientation='auto',
+      showThresholdLabels=false,
+      showThresholdMarkers=true,
+      values=false,
+    ):: self {}
+        + { options+: { reduceOptions+: { [if calcs != null then 'calcs']: calcs } } }
+        + { options+: { reduceOptions+: { [if fields != null then 'fields']: fields } } }
+        + { options+: { [if orientation != null then 'orientation']: orientation } }
+        + { options+: { [if showThresholdLabels != null then 'showThresholdLabels']: showThresholdLabels } }
+        + { options+: { [if showThresholdMarkers != null then 'showThresholdMarkers']: showThresholdMarkers } }
+        + { options+: { reduceOptions+: { [if values != null then 'values']: values } } }
+    ,
+
+
+    addDataLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] } } },
+
+    addPanelLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] },
+
+    addMapping(
+      from=null,
+      id=null,
+      operator=null,
+      text=null,
+      to=null,
+      type=null,
+      value=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { mappings+: [
+          {
+            [if from != null then 'from']: from,
+            [if id != null then 'id']: id,
+            [if operator != null then 'operator']: operator,
+            [if text != null then 'text']: text,
+            [if to != null then 'to']: to,
+            [if type != null then 'type']: type,
+            [if value != null then 'value']: value,
+          },
+        ] } } },
+
+    addOverride(
+      matcher=null,
+      properties=null,
+    ):: self {}
+        + { fieldConfig+: { overrides+: [
+          {
+            [if matcher != null then 'matcher']: matcher,
+            [if properties != null then 'properties']: properties,
+          },
+        ] } },
+
+    addThresholdStep(
+      color=null,
+      value=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { thresholds+: { steps+: [
+          {
+            [if color != null then 'color']: color,
+            [if value != null then 'value']: value,
+          },
+        ] } } } },
+
+    addTarget(
+      target
+    ):: self {}
+        + { targets+: [
+          target,
+        ] },
+
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/panel/graph.libsonnet b/ops/monitoring/lib/grafonnet/panel/graph.libsonnet
new file mode 100644
index 0000000..34985a1
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/panel/graph.libsonnet
@@ -0,0 +1,257 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    bars=false,
+    dashLength=10,
+    dashes=false,
+    datasource='default',
+    decimals=null,
+    description=null,
+    fill=1,
+    fillGradient=0,
+    hiddenSeries=false,
+    lines=true,
+    linewidth=1,
+    nullPointMode='null',
+    percentage=false,
+    pointradius=null,
+    points=false,
+    repeat=null,
+    repeatDirection=null,
+    spaceLength=10,
+    stack=false,
+    steppedLine=false,
+    timeFrom=null,
+    timeShift=null,
+    title=null,
+    transparent=false,
+  ):: {
+    [if bars != null then 'bars']: bars,
+    [if dashLength != null then 'dashLength']: dashLength,
+    [if dashes != null then 'dashes']: dashes,
+    [if datasource != null then 'datasource']: datasource,
+    [if decimals != null then 'decimals']: decimals,
+    [if description != null then 'description']: description,
+    [if fill != null then 'fill']: fill,
+    [if fillGradient != null then 'fillGradient']: fillGradient,
+    [if hiddenSeries != null then 'hiddenSeries']: hiddenSeries,
+    [if lines != null then 'lines']: lines,
+    [if linewidth != null then 'linewidth']: linewidth,
+    [if nullPointMode != null then 'nullPointMode']: nullPointMode,
+    [if percentage != null then 'percentage']: percentage,
+    [if pointradius != null then 'pointradius']: pointradius,
+    [if points != null then 'points']: points,
+    [if repeat != null then 'repeat']: repeat,
+    [if repeatDirection != null then 'repeatDirection']: repeatDirection,
+    [if spaceLength != null then 'spaceLength']: spaceLength,
+    [if stack != null then 'stack']: stack,
+    [if steppedLine != null then 'steppedLine']: steppedLine,
+    [if timeFrom != null then 'timeFrom']: timeFrom,
+    [if timeShift != null then 'timeShift']: timeShift,
+    [if title != null then 'title']: title,
+    [if transparent != null then 'transparent']: transparent,
+    renderer: 'flot',
+    type: 'graph',
+    tooltip+: { value_type: 'individual' },
+
+    setGridPos(
+      h=8,
+      w=12,
+      x=null,
+      y=null,
+    ):: self {}
+        + { gridPos+: { [if h != null then 'h']: h } }
+        + { gridPos+: { [if w != null then 'w']: w } }
+        + { gridPos+: { [if x != null then 'x']: x } }
+        + { gridPos+: { [if y != null then 'y']: y } }
+    ,
+
+    setLegend(
+      alignAsTable=null,
+      avg=false,
+      current=false,
+      max=false,
+      min=false,
+      rightSide=false,
+      show=true,
+      sideWidth=null,
+      total=false,
+      values=true,
+    ):: self {}
+        + { legend+: { [if alignAsTable != null then 'alignAsTable']: alignAsTable } }
+        + { legend+: { [if avg != null then 'avg']: avg } }
+        + { legend+: { [if current != null then 'current']: current } }
+        + { legend+: { [if max != null then 'max']: max } }
+        + { legend+: { [if min != null then 'min']: min } }
+        + { legend+: { [if rightSide != null then 'rightSide']: rightSide } }
+        + { legend+: { [if show != null then 'show']: show } }
+        + { legend+: { [if sideWidth != null then 'sideWidth']: sideWidth } }
+        + { legend+: { [if total != null then 'total']: total } }
+        + { legend+: { [if values != null then 'values']: values } }
+    ,
+
+    setThresholds(
+      thresholdMode='absolute',
+    ):: self {}
+        + { thresholds+: { [if thresholdMode != null then 'mode']: thresholdMode } }
+    ,
+
+    setTooltip(
+      shared=true,
+      sort=2,
+    ):: self {}
+        + { tooltip+: { [if shared != null then 'shared']: shared } }
+        + { tooltip+: { [if sort != null then 'sort']: sort } }
+    ,
+
+    setXaxis(
+      buckets=null,
+      mode='time',
+      name=null,
+      show=true,
+    ):: self {}
+        + { xaxis+: { [if buckets != null then 'buckets']: buckets } }
+        + { xaxis+: { [if mode != null then 'mode']: mode } }
+        + { xaxis+: { [if name != null then 'name']: name } }
+        + { xaxis+: { [if show != null then 'show']: show } }
+    ,
+
+    setYaxis(
+      align=false,
+      alignLevel=0,
+    ):: self {}
+        + { yaxis+: { [if align != null then 'align']: align } }
+        + { yaxis+: { [if alignLevel != null then 'alignLevel']: alignLevel } }
+    ,
+
+
+    addDataLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { options+: { dataLinks+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] } },
+
+    addPanelLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] },
+
+    addOverride(
+      matcher=null,
+      properties=null,
+    ):: self {}
+        + { fieldConfig+: { overrides+: [
+          {
+            [if matcher != null then 'matcher']: matcher,
+            [if properties != null then 'properties']: properties,
+          },
+        ] } },
+
+    addSeriesOverride(
+      alias=null,
+      bars=null,
+      color=null,
+      dashLength=null,
+      dashes=null,
+      fill=null,
+      fillBelowTo=null,
+      fillGradient=null,
+      hiddenSeries=null,
+      hideTooltip=null,
+      legend=null,
+      lines=null,
+      linewidth=null,
+      nullPointMode=null,
+      pointradius=null,
+      points=null,
+      spaceLength=null,
+      stack=null,
+      steppedLine=null,
+      transform=null,
+      yaxis=null,
+      zindex=null,
+    ):: self {}
+        + { seriesOverrides+: [
+          {
+            [if alias != null then 'alias']: alias,
+            [if bars != null then 'bars']: bars,
+            [if color != null then 'color']: color,
+            [if dashLength != null then 'dashLength']: dashLength,
+            [if dashes != null then 'dashes']: dashes,
+            [if fill != null then 'fill']: fill,
+            [if fillBelowTo != null then 'fillBelowTo']: fillBelowTo,
+            [if fillGradient != null then 'fillGradient']: fillGradient,
+            [if hiddenSeries != null then 'hiddenSeries']: hiddenSeries,
+            [if hideTooltip != null then 'hideTooltip']: hideTooltip,
+            [if legend != null then 'legend']: legend,
+            [if lines != null then 'lines']: lines,
+            [if linewidth != null then 'linewidth']: linewidth,
+            [if nullPointMode != null then 'nullPointMode']: nullPointMode,
+            [if pointradius != null then 'pointradius']: pointradius,
+            [if points != null then 'points']: points,
+            [if spaceLength != null then 'spaceLength']: spaceLength,
+            [if stack != null then 'stack']: stack,
+            [if steppedLine != null then 'steppedLine']: steppedLine,
+            [if transform != null then 'transform']: transform,
+            [if yaxis != null then 'yaxis']: yaxis,
+            [if zindex != null then 'zindex']: zindex,
+          },
+        ] },
+
+    addThresholdStep(
+      color=null,
+      value=null,
+    ):: self {}
+        + { thresholds+: { steps+: [
+          {
+            [if color != null then 'color']: color,
+            [if value != null then 'value']: value,
+          },
+        ] } },
+
+    addTarget(
+      target
+    ):: self {}
+        + { targets+: [
+          target,
+        ] },
+
+    addYaxis(
+      decimals=null,
+      format='short',
+      label=null,
+      logBase=1,
+      max=null,
+      min=null,
+      show=true,
+    ):: self {}
+        + { yaxes+: [
+          {
+            [if decimals != null then 'decimals']: decimals,
+            [if format != null then 'format']: format,
+            [if label != null then 'label']: label,
+            [if logBase != null then 'logBase']: logBase,
+            [if max != null then 'max']: max,
+            [if min != null then 'min']: min,
+            [if show != null then 'show']: show,
+          },
+        ] },
+
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/panel/row.libsonnet b/ops/monitoring/lib/grafonnet/panel/row.libsonnet
new file mode 100644
index 0000000..e8a21d3
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/panel/row.libsonnet
@@ -0,0 +1,45 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    collapse=true,
+    collapsed=true,
+    datasource=null,
+    repeat=null,
+    repeatIteration=null,
+    showTitle=true,
+    title=null,
+    titleSize='h6',
+  ):: {
+    [if collapse != null then 'collapse']: collapse,
+    [if collapsed != null then 'collapsed']: collapsed,
+    [if datasource != null then 'datasource']: datasource,
+    [if repeat != null then 'repeat']: repeat,
+    [if repeatIteration != null then 'repeatIteration']: repeatIteration,
+    [if showTitle != null then 'showTitle']: showTitle,
+    [if title != null then 'title']: title,
+    [if titleSize != null then 'titleSize']: titleSize,
+    type: 'row',
+
+    setGridPos(
+      h=8,
+      w=12,
+      x=null,
+      y=null,
+    ):: self {}
+        + { gridPos+: { [if h != null then 'h']: h } }
+        + { gridPos+: { [if w != null then 'w']: w } }
+        + { gridPos+: { [if x != null then 'x']: x } }
+        + { gridPos+: { [if y != null then 'y']: y } }
+    ,
+
+
+    addPanel(
+      panel
+    ):: self {}
+        + { panels+: [
+          panel,
+        ] },
+
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/panel/stat.libsonnet b/ops/monitoring/lib/grafonnet/panel/stat.libsonnet
new file mode 100644
index 0000000..a14c938
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/panel/stat.libsonnet
@@ -0,0 +1,142 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    datasource='default',
+    description=null,
+    repeat=null,
+    repeatDirection=null,
+    title=null,
+    transparent=false,
+  ):: {
+    [if datasource != null then 'datasource']: datasource,
+    [if description != null then 'description']: description,
+    [if repeat != null then 'repeat']: repeat,
+    [if repeatDirection != null then 'repeatDirection']: repeatDirection,
+    [if title != null then 'title']: title,
+    [if transparent != null then 'transparent']: transparent,
+    type: 'stat',
+
+    setFieldConfig(
+      max=null,
+      min=null,
+      thresholdMode='absolute',
+      unit=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { [if max != null then 'max']: max } } }
+        + { fieldConfig+: { defaults+: { [if min != null then 'min']: min } } }
+        + { fieldConfig+: { defaults+: { thresholds+: { [if thresholdMode != null then 'mode']: thresholdMode } } } }
+        + { fieldConfig+: { defaults+: { [if unit != null then 'unit']: unit } } }
+    ,
+
+    setGridPos(
+      h=8,
+      w=12,
+      x=null,
+      y=null,
+    ):: self {}
+        + { gridPos+: { [if h != null then 'h']: h } }
+        + { gridPos+: { [if w != null then 'w']: w } }
+        + { gridPos+: { [if x != null then 'x']: x } }
+        + { gridPos+: { [if y != null then 'y']: y } }
+    ,
+
+    setOptions(
+      calcs=['mean'],
+      colorMode='value',
+      fields=null,
+      graphMode='none',
+      justifyMode='auto',
+      orientation='auto',
+      textMode='auto',
+      values=false,
+    ):: self {}
+        + { options+: { reduceOptions+: { [if calcs != null then 'calcs']: calcs } } }
+        + { options+: { [if colorMode != null then 'colorMode']: colorMode } }
+        + { options+: { reduceOptions+: { [if fields != null then 'fields']: fields } } }
+        + { options+: { [if graphMode != null then 'graphMode']: graphMode } }
+        + { options+: { [if justifyMode != null then 'justifyMode']: justifyMode } }
+        + { options+: { [if orientation != null then 'orientation']: orientation } }
+        + { options+: { [if textMode != null then 'textMode']: textMode } }
+        + { options+: { reduceOptions+: { [if values != null then 'values']: values } } }
+    ,
+
+
+    addPanelLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] },
+
+    addDataLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] } } },
+
+    addMapping(
+      from=null,
+      id=null,
+      operator=null,
+      text=null,
+      to=null,
+      type=null,
+      value=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { mappings+: [
+          {
+            [if from != null then 'from']: from,
+            [if id != null then 'id']: id,
+            [if operator != null then 'operator']: operator,
+            [if text != null then 'text']: text,
+            [if to != null then 'to']: to,
+            [if type != null then 'type']: type,
+            [if value != null then 'value']: value,
+          },
+        ] } } },
+
+    addOverride(
+      matcher=null,
+      properties=null,
+    ):: self {}
+        + { fieldConfig+: { overrides+: [
+          {
+            [if matcher != null then 'matcher']: matcher,
+            [if properties != null then 'properties']: properties,
+          },
+        ] } },
+
+    addThresholdStep(
+      color=null,
+      value=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { thresholds+: { steps+: [
+          {
+            [if color != null then 'color']: color,
+            [if value != null then 'value']: value,
+          },
+        ] } } } },
+
+    addTarget(
+      target
+    ):: self {}
+        + { targets+: [
+          target,
+        ] },
+
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/panel/table.libsonnet b/ops/monitoring/lib/grafonnet/panel/table.libsonnet
new file mode 100644
index 0000000..1bd2152
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/panel/table.libsonnet
@@ -0,0 +1,134 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    datasource='default',
+    description=null,
+    repeat=null,
+    repeatDirection=null,
+    title=null,
+    transparent=false,
+  ):: {
+    [if datasource != null then 'datasource']: datasource,
+    [if description != null then 'description']: description,
+    [if repeat != null then 'repeat']: repeat,
+    [if repeatDirection != null then 'repeatDirection']: repeatDirection,
+    [if title != null then 'title']: title,
+    [if transparent != null then 'transparent']: transparent,
+    type: 'table',
+
+    setFieldConfig(
+      displayName=null,
+      max=0,
+      min=0,
+      thresholdMode='absolute',
+      noValue=null,
+      unit='short',
+      width=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { [if displayName != null then 'displayName']: displayName } } }
+        + { fieldConfig+: { defaults+: { [if max != null then 'max']: max } } }
+        + { fieldConfig+: { defaults+: { [if min != null then 'min']: min } } }
+        + { fieldConfig+: { defaults+: { thresholds+: { [if thresholdMode != null then 'mode']: thresholdMode } } } }
+        + { fieldConfig+: { defaults+: { [if noValue != null then 'noValue']: noValue } } }
+        + { fieldConfig+: { defaults+: { [if unit != null then 'unit']: unit } } }
+        + { fieldConfig+: { defaults+: { custom+: { [if width != null then 'width']: width } } } }
+    ,
+
+    setGridPos(
+      h=8,
+      w=12,
+      x=null,
+      y=null,
+    ):: self {}
+        + { gridPos+: { [if h != null then 'h']: h } }
+        + { gridPos+: { [if w != null then 'w']: w } }
+        + { gridPos+: { [if x != null then 'x']: x } }
+        + { gridPos+: { [if y != null then 'y']: y } }
+    ,
+
+    setOptions(
+      showHeader=true,
+    ):: self {}
+        + { options+: { [if showHeader != null then 'showHeader']: showHeader } }
+    ,
+
+
+    addDataLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] } } },
+
+    addPanelLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] },
+
+    addMapping(
+      from=null,
+      id=null,
+      operator=null,
+      text=null,
+      to=null,
+      type=null,
+      value=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { mappings+: [
+          {
+            [if from != null then 'from']: from,
+            [if id != null then 'id']: id,
+            [if operator != null then 'operator']: operator,
+            [if text != null then 'text']: text,
+            [if to != null then 'to']: to,
+            [if type != null then 'type']: type,
+            [if value != null then 'value']: value,
+          },
+        ] } } },
+
+    addOverride(
+      matcher=null,
+      properties=null,
+    ):: self {}
+        + { fieldConfig+: { overrides+: [
+          {
+            [if matcher != null then 'matcher']: matcher,
+            [if properties != null then 'properties']: properties,
+          },
+        ] } },
+
+    addThresholdStep(
+      color=null,
+      value=null,
+    ):: self {}
+        + { fieldConfig+: { defaults+: { thresholds+: { steps+: [
+          {
+            [if color != null then 'color']: color,
+            [if value != null then 'value']: value,
+          },
+        ] } } } },
+
+    addTarget(
+      target
+    ):: self {}
+        + { targets+: [
+          target,
+        ] },
+
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/panel/text.libsonnet b/ops/monitoring/lib/grafonnet/panel/text.libsonnet
new file mode 100644
index 0000000..1c4c682
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/panel/text.libsonnet
@@ -0,0 +1,58 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    content=null,
+    datasource='default',
+    description=null,
+    mode='markdown',
+    repeat=null,
+    repeatDirection=null,
+    title=null,
+    transparent=false,
+  ):: {
+    [if content != null then 'content']: content,
+    [if datasource != null then 'datasource']: datasource,
+    [if description != null then 'description']: description,
+    [if mode != null then 'mode']: mode,
+    [if repeat != null then 'repeat']: repeat,
+    [if repeatDirection != null then 'repeatDirection']: repeatDirection,
+    [if title != null then 'title']: title,
+    [if transparent != null then 'transparent']: transparent,
+    type: 'text',
+
+    setGridPos(
+      h=8,
+      w=12,
+      x=null,
+      y=null,
+    ):: self {}
+        + { gridPos+: { [if h != null then 'h']: h } }
+        + { gridPos+: { [if w != null then 'w']: w } }
+        + { gridPos+: { [if x != null then 'x']: x } }
+        + { gridPos+: { [if y != null then 'y']: y } }
+    ,
+
+
+    addPanelLink(
+      targetBlank=true,
+      title=null,
+      url=null,
+    ):: self {}
+        + { links+: [
+          {
+            [if targetBlank != null then 'targetBlank']: targetBlank,
+            [if title != null then 'title']: title,
+            [if url != null then 'url']: url,
+          },
+        ] },
+
+    addTarget(
+      target
+    ):: self {}
+        + { targets+: [
+          target,
+        ] },
+
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/target/prometheus.libsonnet b/ops/monitoring/lib/grafonnet/target/prometheus.libsonnet
new file mode 100644
index 0000000..5bfe65d
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/target/prometheus.libsonnet
@@ -0,0 +1,19 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    datasource='default',
+    expr=null,
+    format='time_series',
+    interval=null,
+    intervalFactor=null,
+    legendFormat=null,
+  ):: {
+    [if datasource != null then 'datasource']: datasource,
+    [if expr != null then 'expr']: expr,
+    [if format != null then 'format']: format,
+    [if interval != null then 'interval']: interval,
+    [if intervalFactor != null then 'intervalFactor']: intervalFactor,
+    [if legendFormat != null then 'legendFormat']: legendFormat,
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/template/datasource.libsonnet b/ops/monitoring/lib/grafonnet/template/datasource.libsonnet
new file mode 100644
index 0000000..0bdaf83
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/template/datasource.libsonnet
@@ -0,0 +1,36 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    hide=0,
+    includeAll=false,
+    label=null,
+    multi=false,
+    name=null,
+    query=null,
+    refresh=1,
+    regex=null,
+    skipUrlSync=false,
+  ):: {
+    [if hide != null then 'hide']: hide,
+    [if includeAll != null then 'includeAll']: includeAll,
+    [if label != null then 'label']: label,
+    [if multi != null then 'multi']: multi,
+    [if name != null then 'name']: name,
+    [if query != null then 'query']: query,
+    [if refresh != null then 'refresh']: refresh,
+    [if regex != null then 'regex']: regex,
+    [if skipUrlSync != null then 'skipUrlSync']: skipUrlSync,
+    type: 'datasource',
+
+    setCurrent(
+      selected=false,
+      text=null,
+      value=null,
+    ):: self {}
+        + { current+: { [if selected != null then 'selected']: selected } }
+        + { current+: { [if text != null then 'text']: text } }
+        + { current+: { [if value != null then 'value']: value } },
+
+  },
+}
diff --git a/ops/monitoring/lib/grafonnet/template/query.libsonnet b/ops/monitoring/lib/grafonnet/template/query.libsonnet
new file mode 100644
index 0000000..951cef7
--- /dev/null
+++ b/ops/monitoring/lib/grafonnet/template/query.libsonnet
@@ -0,0 +1,52 @@
+// This file was generated by https://github.com/grafana/dashboard-spec
+
+{
+  new(
+    allValue=null,
+    datasource=null,
+    definition=null,
+    hide=0,
+    includeAll=false,
+    label=null,
+    multi=false,
+    name=null,
+    query=null,
+    refresh=0,
+    regex=null,
+    skipUrlSync=false,
+    sort=0,
+    tagValuesQuery=null,
+    tags=null,
+    tagsQuery=null,
+    useTags=false,
+  ):: {
+    [if allValue != null then 'allValue']: allValue,
+    [if datasource != null then 'datasource']: datasource,
+    [if definition != null then 'definition']: definition,
+    [if hide != null then 'hide']: hide,
+    [if includeAll != null then 'includeAll']: includeAll,
+    [if label != null then 'label']: label,
+    [if multi != null then 'multi']: multi,
+    [if name != null then 'name']: name,
+    [if query != null then 'query']: query,
+    [if refresh != null then 'refresh']: refresh,
+    [if regex != null then 'regex']: regex,
+    [if skipUrlSync != null then 'skipUrlSync']: skipUrlSync,
+    [if sort != null then 'sort']: sort,
+    [if tagValuesQuery != null then 'tagValuesQuery']: tagValuesQuery,
+    [if tags != null then 'tags']: tags,
+    [if tagsQuery != null then 'tagsQuery']: tagsQuery,
+    [if useTags != null then 'useTags']: useTags,
+    type: 'query',
+
+    setCurrent(
+      selected=null,
+      text=null,
+      value=null,
+    ):: self {}
+        + { current+: { [if selected != null then 'selected']: selected } }
+        + { current+: { [if text != null then 'text']: text } }
+        + { current+: { [if value != null then 'value']: value } },
+
+  },
+}
diff --git a/ops/monitoring/secrets/cipher/global-agent-cluster-k0 b/ops/monitoring/secrets/cipher/global-agent-cluster-k0
new file mode 100644
index 0000000..29e715b
--- /dev/null
+++ b/ops/monitoring/secrets/cipher/global-agent-cluster-k0
@@ -0,0 +1,40 @@
+-----BEGIN PGP MESSAGE-----
+
+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
+=e/F1
+-----END PGP MESSAGE-----
diff --git a/ops/monitoring/secrets/cipher/global-agent-grafana b/ops/monitoring/secrets/cipher/global-agent-grafana
new file mode 100644
index 0000000..fd501e6
--- /dev/null
+++ b/ops/monitoring/secrets/cipher/global-agent-grafana
@@ -0,0 +1,40 @@
+-----BEGIN PGP MESSAGE-----
+
+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
+xL1OhySubTvzg2yTCfUoEuMWZFL1rwgT9lrKVg==
+=Do8R
+-----END PGP MESSAGE-----
diff --git a/ops/monitoring/secrets/plain/.gitignore b/ops/monitoring/secrets/plain/.gitignore
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/ops/monitoring/secrets/plain/.gitignore
@@ -0,0 +1 @@
+*
diff --git a/personal/implr/vpn/vpn.jsonnet b/personal/implr/vpn/vpn.jsonnet
new file mode 100644
index 0000000..3f39231
--- /dev/null
+++ b/personal/implr/vpn/vpn.jsonnet
@@ -0,0 +1,37 @@
+local kube = import "../../../kube/kube.libsonnet";
+local vpn = import "vpn.libsonnet";
+
+{
+    local top = self,
+    tls: vpn.PKI("implr-vpn"),
+
+    servers: {
+        praisethesun: vpn.Server("openvpn-implr-praisethesun", 11223, top.tls) {
+            cfg+: {
+                namespace: "implr-vpn",
+                configFile: |||
+                    dev tun
+                    tmp-dir /dev/shm/
+                    proto udp
+                    port 11223
+                    topology subnet
+                    server 172.17.1.0 255.255.255.0
+                    keepalive 10 60
+                    persist-tun
+                    persist-key
+                    compress lz4
+                    cipher AES-256-CBC
+                    dh none
+                    ca /mnt/pki/ca.crt
+                    cert /mnt/pki/tls.crt
+                    key /mnt/pki/tls.key
+                |||
+            }
+        },
+    },
+    clients: {
+        kektop: vpn.Client("kektop", top.servers.praisethesun),
+        admin1: vpn.Client("admin1", top.servers.praisethesun),
+        desk1: vpn.Client("desk1", top.servers.praisethesun),
+    }
+}
diff --git a/personal/implr/vpn/vpn.libsonnet b/personal/implr/vpn/vpn.libsonnet
new file mode 100644
index 0000000..70873b3
--- /dev/null
+++ b/personal/implr/vpn/vpn.libsonnet
@@ -0,0 +1,169 @@
+local kube = import "../../../kube/kube.libsonnet";
+
+{
+    PKI(namespace):: {
+        local env = self,
+        namespace:: namespace,
+        selfSignedIssuer: kube.Issuer("pki-selfsigned") {
+            metadata+: {
+                namespace: env.namespace,
+            },
+            spec: {
+                selfSigned: {},
+            },
+        },
+        selfSignedCert: kube.Certificate("pki-selfsigned") {
+            metadata+: {
+                namespace: env.namespace,
+            },
+            spec: {
+                secretName: "pki-selfsigned-cert",
+                duration: "43800h0m0s", // 5 years,
+                isCA: true,
+                issuerRef: {
+                    name: env.selfSignedIssuer.metadata.name,
+                },
+                commonName: "pki-ca",
+            },
+        },
+        issuer: kube.Issuer("pki-ca") {
+            metadata+: {
+                namespace: env.namespace,
+            },
+            spec: {
+                ca: {
+                    secretName: env.selfSignedCert.spec.secretName,
+                },
+            },
+        },
+    },
+
+    Client(name, server):: {
+        local client = self,
+        metadata:: {
+            namespace: server.cfg.namespace,
+        },
+        cert: kube.Certificate(name + "-cert") {
+                metadata+: client.metadata,
+        
+                spec: {
+                    secretName: name + "-cert",
+                    duration: "35040h0m0s", // 4 years
+                    issuerRef: {
+                        name: server.pki.issuer.metadata.name,
+                        kind: "Issuer",
+                    },
+                    commonName: "client-%s.%s" % [name, server.cfg.namespace],
+                },
+        },
+        
+    },
+
+    Server(name, port, pki):: {
+        local server = self,
+        local cfg = server.cfg,
+
+        pki: pki,
+
+        cfg:: {
+            namespace: error "namespace must be set",
+            storageClassName: "waw-hdd-redundant-3",
+
+            image: "nixery.dev/shell/openvpn",
+            configFile: error "configFile must be set",
+
+        },
+        namespace: kube.Namespace(cfg.namespace),
+
+        metadata:: {
+            namespace: cfg.namespace,
+        },
+
+        config: kube.ConfigMap(name + "-config") {
+            metadata+: server.metadata,
+            data: {
+                "openvpn.conf": cfg.configFile,
+            }
+        },
+
+        cert: kube.Certificate(name + "-cert") {
+                metadata+: server.metadata,
+        
+                spec: {
+                    secretName: name + "-cert",
+                    duration: "35040h0m0s", // 4 years
+                    issuerRef: {
+                        name: pki.issuer.metadata.name,
+                        kind: "Issuer",
+                    },
+                    commonName: "server.%s.%s" % [name, cfg.namespace],
+                    //dnsNames: [
+                        //"%s" % [component.svc.metadata.name ],
+                        //"%s.%s" % [component.svc.metadata.name, component.svc.metadata.namespace ],
+                        //"%s.%s.svc" % [component.svc.metadata.name, component.svc.metadata.namespace ],
+                        //"%s.%s.svc.cluster.local" % [component.svc.metadata.name, component.svc.metadata.namespace ],
+                        //"%s.%s.svc.%s" % [component.svc.metadata.name, component.svc.metadata.namespace, env.pkiClusterFQDN ],
+                    //],
+                },
+            },
+
+
+        deployment: kube.Deployment(name) {
+            metadata+: server.metadata,
+            spec+: {
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.ConfigMapVolume(server.config),
+                            pki: {
+                                secret: { secretName: server.cert.spec.secretName },
+                            },
+                        },
+
+                        containers_: {
+                            server: kube.Container("server") {
+                                image: cfg.image,
+                                env_: {
+                                },
+                                command: [
+                                    "/bin/openvpn", "--config", "/config/openvpn.conf"
+                                ],
+                                ports_: {
+                                    client: { containerPort: port },
+                                },
+                                volumeMounts_: {
+                                    config: { mountPath: "/config" },
+                                    pki: { mountPath: "/mnt/pki" },
+                                },
+                                resources: {
+                                    requests: {
+                                        cpu: "250m",
+                                        memory: "100Mi",
+                                    },
+                                    limits: {
+                                        cpu: "500m",
+                                        memory: "512Mi",
+                                    },
+                                },
+                                securityContext: {
+                                    privileged: true,
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+        svc: kube.Service(name) {
+            metadata+: server.metadata,
+            target_pod:: server.deployment.spec.template,
+            spec+: {
+                ports: [
+                    { name: "client", port: port, targetPort: port, protocol: "UDP" },
+                ],
+                type: "LoadBalancer",
+                externalTrafficPolicy: "Local",
+            },
+        },
+    },
+}
diff --git a/personal/q3k/BUILD b/personal/q3k/BUILD
index 99e661d..8317e7c 100644
--- a/personal/q3k/BUILD
+++ b/personal/q3k/BUILD
@@ -1,7 +1,9 @@
+load("@pydeps//:requirements.bzl", "requirement")
+
 py_binary(
     name = "django-admin",
     srcs = ["django-admin.py"],
     deps = [
-        "@pydeps36//django",
+        requirement("django"),
     ]
 )
diff --git a/personal/q3k/djtest/BUILD b/personal/q3k/djtest/BUILD
index b3a2cad..8e82b25 100644
--- a/personal/q3k/djtest/BUILD
+++ b/personal/q3k/djtest/BUILD
@@ -1,8 +1,12 @@
+load("@pydeps//:requirements.bzl", "requirement")
+
 py_library(
     name = "app",
     srcs = glob(["djtest/**/*.py"]),
     deps = [
-        "@pydeps//django",
+        requirement("django"),
+        requirement("pytz"),
+        requirement("sqlparse"),
     ],
 )
 
@@ -20,8 +24,7 @@
     deps = [
        ":app",
        "@bazel_tools//tools/python/runfiles",
-    ],
-    data = [
-        '@pip__uWSGI_2_0_18_cp36_cp36m_linux_x86_64//scripts:uwsgi',
+        requirement("uwsgi"),
+        requirement("pyelftools"),
     ],
 )
diff --git a/personal/q3k/djtest/uwsgi-start.py b/personal/q3k/djtest/uwsgi-start.py
index 0edcdaf..a5c325e 100644
--- a/personal/q3k/djtest/uwsgi-start.py
+++ b/personal/q3k/djtest/uwsgi-start.py
@@ -6,13 +6,24 @@
 import bazel_tools
 import bazel_tools.tools.python
 
+from elftools.elf.elffile import ELFFile
+from elftools.elf.segments import InterpSegment
+
 from bazel_tools.tools.python.runfiles import runfiles
 r = runfiles.Create()
 
-uwsgi = r.Rlocation("pip__uWSGI_2_0_18_cp36_cp36m_linux_x86_64/scripts/uwsgi")
-print(uwsgi)
+uwsgi = r.Rlocation("pydeps_pypi__uWSGI_2_0_18/uWSGI-2.0.18.data/scripts/uwsgi")
 settings = r.Rlocation("hscloud/personal/q3k/djtest/djtest/settings.py")
-print(settings)
+
+# uwsgi from runfiles is non-chmodded, we have to run it through its interpreter
+ld = None
+with open(uwsgi, 'rb') as f:
+    elffile = ELFFile(f)
+    for segment in elffile.iter_segments():
+        if isinstance(segment, InterpSegment):
+            ld = segment.get_interp_name()
+if ld is None:
+    raise Exception("could not find interpreter/ld.so path in uwsgi - failing")
 
 apppath = os.path.dirname(settings)
 sitepath = os.path.dirname(apppath)
@@ -35,8 +46,7 @@
 cfgf.close()
 
 args = [
-    # uwsgi from runfiles is non-chmodded, run through interpreter
-    '/lib64/ld-linux-x86-64.so.2',
+    ld,
     uwsgi,
     '--ini', cfgf.name,
 ]
diff --git a/personal/q3k/factorio/BUILD b/personal/q3k/factorio/BUILD
deleted file mode 100644
index 39b373b..0000000
--- a/personal/q3k/factorio/BUILD
+++ /dev/null
@@ -1,56 +0,0 @@
-load("@io_bazel_rules_docker//container:container.bzl", "container_image")
-load("@io_bazel_rules_docker//container:container.bzl", "container_push")
-
-container_image(
-    name="0.16.51-1",
-    base="@prodimage-bionic//image",
-    tars = ["@factorio-headless-0.16.51//file"],
-    files = [":entrypoint.sh"],
-    directory = "/",
-    entrypoint = ["/entrypoint.sh"],
-)
-
-container_image(
-    name="0.17.41-1",
-    base="@prodimage-bionic//image",
-    tars = ["@factorio-headless-0.17.41//file"],
-    files = [":entrypoint.sh"],
-    directory = "/",
-    entrypoint = ["/entrypoint.sh"],
-)
-
-container_image(
-    name="0.17.52-1",
-    base="@prodimage-bionic//image",
-    tars = ["@factorio-headless-0.17.52//file"],
-    files = [":entrypoint.sh"],
-    directory = "/",
-    entrypoint = ["/entrypoint.sh"],
-)
-
-container_image(
-    name="0.17.79-1",
-    base="@prodimage-bionic//image",
-    tars = ["@factorio-headless-0.17.79//file"],
-    files = [":entrypoint.sh"],
-    directory = "/",
-    entrypoint = ["/entrypoint.sh"],
-)
-
-container_image(
-    name="0.18.12-2",
-    base="@prodimage-bionic//image",
-    tars = ["@factorio-headless-0.18.12//file"],
-    files = [":entrypoint.sh"],
-    directory = "/",
-    entrypoint = ["/entrypoint.sh"],
-)
-
-container_push(
-    name = "push_latest",
-    image = ":0.18.12-2",
-    format = "Docker",
-    registry = "registry.k0.hswaw.net",
-    repository = "app/factorio",
-    tag = "0.18.12-2",
-)
diff --git a/personal/q3k/factorio/kube/factorio.libsonnet b/personal/q3k/factorio/kube/factorio.libsonnet
index be69054..4f2ff39 100644
--- a/personal/q3k/factorio/kube/factorio.libsonnet
+++ b/personal/q3k/factorio/kube/factorio.libsonnet
@@ -11,12 +11,13 @@
         appName: "factorio",
         storageClassName: "waw-hdd-redundant-2",
         prefix: "", # if set, should be 'foo-'
+        proxyImage: error "proxyImage must be set",
 
         rconPort: 2137,
         rconPassword: "farts",
 
         tag: "latest",
-        image: "registry.k0.hswaw.net/app/factorio:" + cfg.tag,
+        image: "registry.k0.hswaw.net/q3k/factorio:" + cfg.tag,
         resources: {
             requests: {
                 cpu: "500m",
@@ -27,6 +28,8 @@
                 memory: "1Gi",
             },
         },
+
+        mods: [],
     },
 
 
@@ -35,7 +38,7 @@
     metadata:: {
         namespace: cfg.namespace,
         labels: {
-            "app.kubernetes.io/name": cfg.appName,
+            "app.kubernetes.io/name": factorio.makeName("factorio"),
             "app.kubernetes.io/managed-by": "kubecfg",
             "app.kubernetes.io/component": "factorio",
         },
@@ -67,6 +70,16 @@
         },
     },
 
+    configMap: kube.ConfigMap(factorio.makeName("config")) {
+        metadata+: factorio.metadata,
+        data: {
+            "mods.pb.text": std.join("\n", [
+                "mod { name: \"%s\" version: \"%s\" }" % [m.name, m.version],
+                for m in cfg.mods
+            ]),
+        },
+    },
+
     deployment: kube.Deployment(factorio.makeName("factorio")) {
         metadata+: factorio.metadata,
         spec+: {
@@ -76,6 +89,23 @@
                     volumes_: {
                         data: kube.PersistentVolumeClaimVolume(factorio.volumeClaimData),
                         mods: kube.PersistentVolumeClaimVolume(factorio.volumeClaimMods),
+                        config: kube.ConfigMapVolume(factorio.configMap),
+                    },
+                    initContainers_: {
+                        modproxy: kube.Container("modproxy") {
+                            image: cfg.proxyImage,
+                            command: [
+                                "/games/factorio/modproxy/client",
+                                "-hspki_disable",
+                                "-factorio_path", "/factorio",
+                                "-proxy", "proxy.factorio.svc.cluster.local:4200",
+                                "-config_path", "/factorio/mods.pb.text",
+                            ],
+                            volumeMounts_: {
+                                mods: { mountPath: "/factorio/mods" },
+                                config: { mountPath: "/factorio/mods.pb.text", subPath: "mods.pb.text" },
+                            },
+                        },
                     },
                     containers_: {
                         factorio: kube.Container(factorio.makeName("factorio")) {
@@ -101,7 +131,12 @@
         },
     },
     svc: kube.Service(factorio.makeName("factorio")) {
-        metadata+: factorio.metadata,
+        metadata+: factorio.metadata {
+            // hack - have to keep existing naming scheme otherwise we'd lose addresses
+            labels: {
+                "app.kubernetes.io/name": cfg.appName,
+            },
+        },
         target_pod:: factorio.deployment.spec.template,
         spec+: {
             ports: [
diff --git a/personal/q3k/factorio/kube/prod.jsonnet b/personal/q3k/factorio/kube/prod.jsonnet
index c1ee8e2..27dd38d 100644
--- a/personal/q3k/factorio/kube/prod.jsonnet
+++ b/personal/q3k/factorio/kube/prod.jsonnet
@@ -3,23 +3,112 @@
 
 
 // Available versions:
-//  - 0.16.51-1
-//  - 0.17.41-1
-//  - 0.17.52-1
-//  - 0.17.79-1
-//  - 0.18.12-2
+//  - 0.18.40-1
+//  - 1.0.0-1
 
 {
     local prod = self,
 
+    proxyImage:: "registry.k0.hswaw.net/games/factorio/modproxy:1589157915-eafe7be328477e8a6590c4210466ef12901f1b9a",
+
     namespace: kube.Namespace("factorio"),
     instance(name, tag):: factorio {
         cfg+: {
             namespace: "factorio",
             prefix: name + "-",
             tag: tag,
+            proxyImage: prod.proxyImage,
         }
     },
 
-    q3k: prod.instance("q3k", "0.18.12-2"),
+    proxy: {
+        pvc: kube.PersistentVolumeClaim("proxy-cas") {
+            metadata+: {
+                namespace: "factorio",
+            },
+            spec+: {
+                storageClassName: "waw-hdd-redundant-3",
+                accessModes: [ "ReadWriteOnce" ],
+                resources: {
+                    requests: {
+                        storage: "32Gi",
+                    },
+                },
+            },
+        },
+        deploy: kube.Deployment("proxy") {
+            metadata+: {
+                namespace: "factorio",
+            },
+            spec+: {
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            cas: kube.PersistentVolumeClaimVolume(prod.proxy.pvc),
+                        },
+                        containers_: {
+                            proxy: kube.Container("proxy") {
+                                image:prod.proxyImage,
+                                command: [
+                                    "/games/factorio/modproxy/modproxy",
+                                    "-hspki_disable",
+                                    "-cas_directory", "/mnt/cas",
+                                    "-listen_address", "0.0.0.0:4200",
+                                ],
+                                volumeMounts_: {
+                                    cas: { mountPath: "/mnt/cas" },
+                                },
+                                ports_: {
+                                    client: { containerPort: 4200 },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+        svc: kube.Service("proxy") {
+            metadata+: {
+                namespace: "factorio",
+            },
+            target_pod:: prod.proxy.deploy.spec.template,
+            spec+: {
+                ports: [
+                    { name: "client", port: 4200, targetPort: 4200, protocol: "TCP" },
+                ],
+            },
+        },
+    },
+
+    local mod = function(name, version) { name: name, version: version },
+
+    q3k: prod.instance("q3k", "1.0.0-1") {
+        cfg+: {
+            mods: [
+                mod("Squeak Through", "1.8.0"),
+                mod("Bottleneck", "0.11.4"),
+            ],
+        },
+    },
+    pymods: prod.instance("pymods", "1.0.0-1") {
+        cfg+: {
+            mods: [
+                mod("Bottleneck", "0.11.4"),
+                mod("FARL", "4.0.2"),
+                mod("Squeak Through", "1.8.0"),
+                mod("pycoalprocessing", "1.8.3"),
+                mod("pycoalprocessinggraphics", "1.0.7"),
+                mod("pyfusionenergy", "1.6.3"),
+                mod("pyfusionenergygraphics", "1.0.5"),
+                mod("pyhightech", "1.6.2"),
+                mod("pyhightechgraphics", "1.0.8"),
+                mod("pyindustry", "1.4.7"),
+                mod("pyrawores", "2.1.5"),
+                mod("pyraworesgraphics", "1.0.4"),
+                mod("rso-mod", "6.0.11"),
+                mod("stdlib", "1.4.3"),
+                mod("what-is-it-really-used-for", "1.5.13"),
+            ],
+        },
+    },
 }
diff --git a/personal/q3k/minecraft/Dockerfile-paper b/personal/q3k/minecraft/Dockerfile-paper
new file mode 100644
index 0000000..048524d
--- /dev/null
+++ b/personal/q3k/minecraft/Dockerfile-paper
@@ -0,0 +1,40 @@
+FROM ubuntu:20.04
+
+RUN set -e -x ;\
+    export DEBIAN_FRONTEND=noninteractive ;\
+    apt-get -y update ;\
+    apt-get -y upgrade ;\
+    apt-get -y install git openjdk-8-jre-headless wget
+
+RUN set -e -x ;\
+    export DEBIAN_FRONTEND=noninteractive ;\
+    apt-get -y install build-essential python3-dev python3-pil python3-numpy ;\
+    mkdir overviewer ;\
+    cd overviewer ;\
+    wget --quiet https://overviewer.org/builds/src/152/overviewer-0.15.77.tar.gz ;\
+    tar xvf *.tar.gz ;\
+    cd Minecraft-Overviewer* ;\
+    python3 setup.py build ;\
+    python3 setup.py install ;\
+    cd ../.. ;\
+    rm -rf overviewer ;\
+    apt-get -y purge python3-dev build-essential
+
+
+RUN set -e -x ;\
+    useradd -rm minecraft
+
+USER minecraft
+WORKDIR /home/minecraft
+ARG VERSION=1.16.1
+
+RUN set -e -x ;\
+    wget --quiet https://papermc.io/api/v1/paper/${VERSION}/latest/download ;\
+    mv download server.jar
+
+ADD worldedit-bukkit-7.2.0-beta-01.jar .
+ADD worldguard-bukkit-7.0.4-beta1.jar .
+
+RUN set -e -x ;\
+    mkdir -p /home/minecraft/.minecraft/versions/${VERSION}/ ;\
+    wget --quiet https://overviewer.org/textures/${VERSION} -O /home/minecraft/.minecraft/versions/${VERSION}/${VERSION}.jar
diff --git a/personal/q3k/minecraft/Dockerfile-spigot b/personal/q3k/minecraft/Dockerfile-spigot
new file mode 100644
index 0000000..2d65017
--- /dev/null
+++ b/personal/q3k/minecraft/Dockerfile-spigot
@@ -0,0 +1,46 @@
+FROM ubuntu:20.04
+
+RUN set -e -x ;\
+    export DEBIAN_FRONTEND=noninteractive ;\
+    apt-get -y update ;\
+    apt-get -y upgrade ;\
+    apt-get -y install git openjdk-8-jre-headless wget
+
+RUN set -e -x ;\
+    export DEBIAN_FRONTEND=noninteractive ;\
+    apt-get -y install build-essential python3-dev python3-pil python3-numpy ;\
+    mkdir overviewer ;\
+    cd overviewer ;\
+    wget --quiet https://overviewer.org/builds/src/152/overviewer-0.15.77.tar.gz ;\
+    tar xvf *.tar.gz ;\
+    cd Minecraft-Overviewer* ;\
+    python3 setup.py build ;\
+    python3 setup.py install ;\
+    cd ../.. ;\
+    rm -rf overviewer ;\
+    apt-get -y purge python3-dev build-essential
+
+
+RUN set -e -x ;\
+    useradd -rm minecraft
+
+USER minecraft
+WORKDIR /home/minecraft
+ARG VERSION=1.16.1
+
+RUN set -e -x ;\
+    mkdir build ;\
+    cd build ;\
+    wget --quiet https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar ;\
+    java -jar BuildTools.jar --rev 1.16.1 ;\
+    cp spigot*jar .. ;\
+    cd .. ;\
+    rm -rf build ;\
+    mv spigot*.jar server.jar
+
+ADD worldedit-bukkit-7.2.0-beta-01.jar .
+ADD worldguard-bukkit-7.0.4-beta1.jar .
+
+RUN set -e -x ;\
+    mkdir -p /home/minecraft/.minecraft/versions/${VERSION}/ ;\
+    wget --quiet https://overviewer.org/textures/${VERSION} -O /home/minecraft/.minecraft/versions/${VERSION}/${VERSION}.jar
diff --git a/personal/q3k/minecraft/LICENSE.thirdparty.txt b/personal/q3k/minecraft/LICENSE.thirdparty.txt
new file mode 100644
index 0000000..4444b16
--- /dev/null
+++ b/personal/q3k/minecraft/LICENSE.thirdparty.txt
@@ -0,0 +1,7 @@
+WorldEdit and WorldGuard are licensed under the terms of the GNU Lesser General
+Public License v3.
+
+Full license texts are available at:
+
+* WorldEdit: https://github.com/EngineHub/WorldEdit/blob/7.1.0/LICENSE.txt
+* WorldGuard: https://github.com/EngineHub/WorldGuard/blob/7.0.2/LICENSE.txt
diff --git a/personal/q3k/minecraft/plugin/BUILD b/personal/q3k/minecraft/plugin/BUILD
new file mode 100644
index 0000000..ac8e49f
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/BUILD
@@ -0,0 +1,14 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["genpluginyml.go"],
+    importpath = "code.hackerspace.pl/personal/q3k/minecraft/plugins/genpluginyml",
+    visibility = ["//visibility:private"],
+)
+
+go_binary(
+    name = "genpluginyml",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
diff --git a/personal/q3k/minecraft/plugin/defs.bzl b/personal/q3k/minecraft/plugin/defs.bzl
new file mode 100644
index 0000000..898b057
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/defs.bzl
@@ -0,0 +1,64 @@
+load("//bzl:rules.bzl", copy_binary="copy_go_binary")
+
+def _plugin_yml_gen_impl(ctx):
+    ctx.actions.run(
+        mnemonic = "PluginYmlGen",
+        progress_message = "Generating plugin.yml",
+
+        inputs = [ctx.info_file],
+        outputs = [ctx.outputs.out],
+        executable = ctx.executable._genpluginyml,
+        arguments = [
+            "-name", ctx.label.name,
+            "-author", ctx.attr.author,
+            "-main", ctx.attr.main,
+            "-version", ctx.attr.version,
+            "-status_file", ctx.info_file.path,
+            "-out_file", ctx.outputs.out.path,
+        ],
+    )
+
+plugin_yml_gen = rule(
+    implementation = _plugin_yml_gen_impl,
+    attrs = {
+        "main": attr.string(mandatory = True),
+        "version": attr.string(default = ""),
+        "author": attr.string(default = "bazel"),
+        "_genpluginyml": attr.label(
+            default = Label("//personal/q3k/minecraft/plugin:genpluginyml"),
+            cfg = "host",
+            executable = True,
+            allow_files = True,
+        ),
+    },
+    outputs = {
+        "out": "plugin.yml",
+    },
+)
+
+def bukkit_plugin(name, srcs, deps, main, author="", version=""):
+    ymlname = name + "-yml"
+    plugin_yml_gen(
+        name = ymlname,
+        author = author,
+        version = version,
+        main = main,
+    )
+
+    jarname = name + "-jar"
+    native.java_binary(
+        name = jarname,
+        create_executable = False,
+        srcs = srcs,
+        deps = deps,
+        classpath_resources = [":" + ymlname],
+    )
+
+    copy_binary(
+        name = name + ".jar",
+        src = ":" + jarname + "_deploy.jar",
+    )
+    native.alias(
+        name = name,
+        actual = ":" + name + ".jar",
+    )
diff --git a/personal/q3k/minecraft/plugin/example/BUILD b/personal/q3k/minecraft/plugin/example/BUILD
new file mode 100644
index 0000000..e3bc13a
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/example/BUILD
@@ -0,0 +1,11 @@
+load("//personal/q3k/minecraft/plugin:defs.bzl", "bukkit_plugin")
+bukkit_plugin(
+    name = "example",
+    main = "hscloud.personal.q3k.minecraft.plugin.example.Main",
+    srcs = [
+        "Main.java"
+    ],
+    deps = [
+        "@maven//:org_spigotmc_spigot_api",
+    ],
+)
diff --git a/personal/q3k/minecraft/plugin/example/Main.java b/personal/q3k/minecraft/plugin/example/Main.java
new file mode 100644
index 0000000..a67ee78
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/example/Main.java
@@ -0,0 +1,15 @@
+package hscloud.personal.q3k.minecraft.plugin.example;
+
+import org.bukkit.plugin.java.JavaPlugin;
+
+public class Main extends JavaPlugin {
+    @Override
+    public void onEnable() {
+        System.out.println("enabled");
+    }
+
+    @Override
+    public void onDisable() {
+        System.out.println("disabled");
+    }
+}
diff --git a/personal/q3k/minecraft/plugin/genpluginyml.go b/personal/q3k/minecraft/plugin/genpluginyml.go
new file mode 100644
index 0000000..1174f8f
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/genpluginyml.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"strings"
+)
+
+var (
+	flagName    string
+	flagAuthor  string
+	flagMain    string
+	flagVersion string
+
+	flagStatusFile string
+	flagOutFile    string
+)
+
+func main() {
+	flag.StringVar(&flagName, "name", "", "plugin name")
+	flag.StringVar(&flagAuthor, "author", "", "plugin author")
+	flag.StringVar(&flagMain, "main", "", "plugin main class")
+	flag.StringVar(&flagVersion, "version", "", "plugin version, if not given, workspace status will be used")
+	flag.StringVar(&flagStatusFile, "status_file", "", "path to workspace status file for version generation")
+	flag.StringVar(&flagOutFile, "out_file", "plugin.yml", "path to output plugin.yml")
+	flag.Parse()
+
+	if flagVersion == "" {
+		status, err := ioutil.ReadFile(flagStatusFile)
+		if err != nil {
+			log.Fatalf("ReadFile(%q): %v", flagStatusFile, err)
+		}
+
+		for _, line := range strings.Split(string(status), "\n") {
+			line = strings.TrimSpace(line)
+			parts := strings.Fields(line)
+			if len(parts) != 2 {
+				continue
+			}
+			if parts[0] == "STABLE_GIT_VERSION" {
+				flagVersion = fmt.Sprintf("hscloud-git-%s", parts[1])
+			}
+		}
+	}
+
+	if flagVersion == "" {
+		log.Fatalf("could not determine version from status")
+	}
+
+	// a yaml is a json, a json is a yaml
+	yml := struct {
+		Name       string `json:"name"`
+		Version    string `json:"version"`
+		Author     string `json:"author"`
+		Main       string `json:"main"`
+		APIVersion string `json:"api-version"`
+	}{
+		Name:       flagName,
+		Version:    flagVersion,
+		Author:     flagAuthor,
+		Main:       flagMain,
+		APIVersion: "1.15",
+	}
+
+	out, err := json.Marshal(&yml)
+	if err != nil {
+		log.Fatalf("marshal: %v", err)
+	}
+	err = ioutil.WriteFile(flagOutFile, out, 0644)
+	if err != nil {
+		log.Fatalf("WriteFile(%q): %v", out, err)
+	}
+}
diff --git a/personal/q3k/minecraft/plugin/hscloud/BUILD b/personal/q3k/minecraft/plugin/hscloud/BUILD
new file mode 100644
index 0000000..d53644b
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/hscloud/BUILD
@@ -0,0 +1,20 @@
+load("//personal/q3k/minecraft/plugin:defs.bzl", "bukkit_plugin")
+
+bukkit_plugin(
+    name = "hscloud",
+    main = "hscloud.personal.q3k.minecraft.plugin.hscloud.Main",
+    author = "q3k",
+    srcs = [
+        "Main.java",
+        "StateSynchronizer.java",
+    ],
+    deps = [
+        "//personal/q3k/minecraft/plugin/hscloud/proto:hscloud_java_grpc",
+        "//personal/q3k/minecraft/plugin/hscloud/proto:hscloud_java_proto",
+        "@maven//:org_spigotmc_spigot_api",
+        "@io_grpc_grpc_java//api",
+        "@io_grpc_grpc_java//stub",
+        "@maven//:io_grpc_grpc_netty_shaded",
+        "@maven//:io_grpc_grpc_services",
+    ],
+)
diff --git a/personal/q3k/minecraft/plugin/hscloud/Main.java b/personal/q3k/minecraft/plugin/hscloud/Main.java
new file mode 100644
index 0000000..242bbb1
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/hscloud/Main.java
@@ -0,0 +1,108 @@
+package hscloud.personal.q3k.minecraft.plugin.hscloud;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.protobuf.services.ProtoReflectionService;
+import io.grpc.stub.StreamObserver;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.scheduler.BukkitScheduler;
+
+import hscloud.personal.q3k.minecraft.plugin.hscloud.proto.IntrospectorGrpc;
+import hscloud.personal.q3k.minecraft.plugin.hscloud.proto.Proto;
+
+
+public class Main extends JavaPlugin {
+    private Server server_;
+    private static final Logger logger_ = Logger.getLogger(Main.class.getName());
+    private static final int port_ = 2137;
+
+    public StateSynchronizer synchronizer_;
+
+    public Main() {
+        synchronizer_ = new StateSynchronizer();
+        server_ = ServerBuilder
+            .forPort(port_)
+            .addService(new IntrospectorService(synchronizer_))
+            .addService(ProtoReflectionService.newInstance())
+            .build();
+    }
+
+    @Override
+    public void onEnable() {
+
+        BukkitScheduler scheduler = getServer().getScheduler();
+        scheduler.scheduleSyncRepeatingTask(this, new Runnable() {
+            @Override
+            public void run() {
+                ArrayList<StateSynchronizer.Player> players = new ArrayList<StateSynchronizer.Player>();
+                for (Player p : Bukkit.getOnlinePlayers()) {
+                    StateSynchronizer.Player pp = new StateSynchronizer.Player(p.getPlayerListName(), p.getUniqueId().toString());
+                    players.add(pp);
+                }
+                synchronizer_.setPlayers(players.toArray(new StateSynchronizer.Player[0]));
+
+                World world = Bukkit.getWorld("world");
+                if (world != null) {
+                    synchronizer_.setTime(world.getTime());
+                }
+            }
+        }, 0, 20L);
+
+        try {
+            server_.start();
+        } catch (IOException e) {
+            logger_.severe("could not start gRPC");
+            e.printStackTrace(System.err);
+            return;
+        }
+        logger_.info("gRPC started, listening on " + port_);
+    }
+
+    @Override
+    public void onDisable() {
+        if (server_ != null) {
+            try {
+                server_.shutdown().awaitTermination(30, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                logger_.severe("could not stop gRPC");
+                e.printStackTrace(System.err);
+            }
+        }
+        server_ = null;
+    }
+
+    public static class IntrospectorService extends IntrospectorGrpc.IntrospectorImplBase {
+        private StateSynchronizer synchronizer_;
+
+        public IntrospectorService(StateSynchronizer synchronizer) {
+            synchronizer_ = synchronizer;
+        }
+
+        @Override
+        public void status(Proto.StatusRequest request, StreamObserver<Proto.StatusResponse> responseObserver) {
+            StateSynchronizer.Player[] players = synchronizer_.getPlayers();
+
+            Proto.StatusResponse.Builder builder = Proto.StatusResponse.newBuilder();
+            builder.setTime(synchronizer_.getTime());
+            for (StateSynchronizer.Player p : players) {
+                builder.addPlayers(Proto.Player.newBuilder()
+                    .setUsername(p.getName())
+                    .setUuid(p.getUUID())
+                    .build());
+
+
+            }
+            responseObserver.onNext(builder.build());
+            responseObserver.onCompleted();
+        }
+    }
+}
diff --git a/personal/q3k/minecraft/plugin/hscloud/StateSynchronizer.java b/personal/q3k/minecraft/plugin/hscloud/StateSynchronizer.java
new file mode 100644
index 0000000..a10f5d5
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/hscloud/StateSynchronizer.java
@@ -0,0 +1,49 @@
+package hscloud.personal.q3k.minecraft.plugin.hscloud;
+
+public class StateSynchronizer {
+    private Player[] players_;
+    private long time_;
+    private Object lock_ = new Object();
+
+    public void setPlayers(Player[] players) {
+        synchronized(lock_) {
+            players_ = players;
+        }
+    }
+
+    public void setTime(long time) {
+        synchronized(lock_) {
+            time_ = time;
+        }
+    }
+
+    public Player[] getPlayers() {
+        synchronized(lock_) {
+            return players_;
+        }
+    }
+
+    public long getTime() {
+        synchronized(lock_) {
+            return time_;
+        }
+    }
+
+    public static class Player {
+        private String name_;
+        private String uuid_;
+
+        public Player(String name, String uuid) {
+            name_ = name;
+            uuid_ = uuid;
+        }
+
+        public String getName() {
+            return name_;
+        }
+
+        public String getUUID() {
+            return uuid_;
+        }
+    }
+}
diff --git a/personal/q3k/minecraft/plugin/hscloud/bridge/BUILD.bazel b/personal/q3k/minecraft/plugin/hscloud/bridge/BUILD.bazel
new file mode 100644
index 0000000..eb05303
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/hscloud/bridge/BUILD.bazel
@@ -0,0 +1,37 @@
+load("@io_bazel_rules_docker//container:container.bzl", "container_image")
+load("@io_bazel_rules_docker//container:container.bzl", "container_push")
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["main.go"],
+    importpath = "code.hackerspace.pl/hscloud/personal/q3k/minecraft/plugin/hscloud/bridge",
+    visibility = ["//visibility:private"],
+    deps = [
+        "//personal/q3k/minecraft/plugin/hscloud/proto:go_default_library",
+        "@org_golang_google_grpc//:go_default_library",
+    ],
+)
+
+go_binary(
+    name = "bridge",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
+
+container_image(
+    name = "bridge_container",
+    base = "@prodimage-bionic//image",
+    files = [":bridge"],
+    directory = "/personal/q3k/minecraft/plugin/hscloud/bridge",
+    entrypoint = "/personal/q3k/minecraft/plugin/hscloud/bridge/bridge",
+)
+
+container_push(
+    name = "bridge_container_push",
+    image = ":bridge_container",
+    format = "Docker",
+    registry = "registry.k0.hswaw.net",
+    repository = "q3k/minecraft-hscloud-bridge",
+    tag = "20200518c",
+)
diff --git a/personal/q3k/minecraft/plugin/hscloud/bridge/main.go b/personal/q3k/minecraft/plugin/hscloud/bridge/main.go
new file mode 100644
index 0000000..f8863be
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/hscloud/bridge/main.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"net/http"
+
+	"google.golang.org/grpc"
+
+	pb "code.hackerspace.pl/hscloud/personal/q3k/minecraft/plugin/hscloud/proto"
+)
+
+var (
+	flagPlugin string
+	flagListen string
+)
+
+func main() {
+	flag.StringVar(&flagPlugin, "plugin", "minecraft.wypierdolzpolski.pl:2137", "address of gRPC plugin")
+	flag.StringVar(&flagListen, "listen", "0.0.0.0:8081", "address to listen at")
+	flag.Parse()
+
+	conn, err := grpc.Dial(flagPlugin, grpc.WithInsecure())
+	if err != nil {
+		log.Fatalf("Dial(%q): %v", flagPlugin, err)
+	}
+
+	client := pb.NewIntrospectorClient(conn)
+
+	http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
+		res, err := client.Status(r.Context(), &pb.StatusRequest{})
+		if err != nil {
+			http.Error(w, "internal server error", 500)
+			log.Printf("Status error: %v", err)
+		}
+
+		fmt.Fprintf(w, "# HELP minecraft_players_online Total number of players online.\n")
+		fmt.Fprintf(w, "# TYPE minecraft_players_online gauge\n")
+		fmt.Fprintf(w, "minecraft_players_online %d\n", len(res.Players))
+	})
+
+	log.Printf("Listening on %s", flagListen)
+	err = http.ListenAndServe(flagListen, nil)
+	if err != nil {
+		log.Fatalf("ListenAndServe: %v", err)
+	}
+}
diff --git a/personal/q3k/minecraft/plugin/hscloud/proto/BUILD b/personal/q3k/minecraft/plugin/hscloud/proto/BUILD
new file mode 100644
index 0000000..903e472
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/hscloud/proto/BUILD
@@ -0,0 +1,38 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library")
+
+java_proto_library(
+    name = "hscloud_java_proto",
+    deps = [":hscloud_proto"],
+    visibility = ["//visibility:public"],
+)
+
+java_grpc_library(
+    name = "hscloud_java_grpc",
+    srcs = [":hscloud_proto"],
+    deps = [":hscloud_java_proto"],
+    visibility = ["//visibility:public"],
+)
+
+proto_library(
+    name = "hscloud_proto",
+    srcs = ["hscloud.proto"],
+    visibility = ["//visibility:public"],
+)
+
+go_proto_library(
+    name = "hscloud_go_proto",
+    compilers = ["@io_bazel_rules_go//proto:go_grpc"],
+    importpath = "code.hackerspace.pl/hscloud/personal/q3k/minecraft/plugin/hscloud/proto",
+    proto = ":hscloud_proto",
+    visibility = ["//visibility:public"],
+)
+
+go_library(
+    name = "go_default_library",
+    embed = [":hscloud_go_proto"],
+    importpath = "code.hackerspace.pl/hscloud/personal/q3k/minecraft/plugin/hscloud/proto",
+    visibility = ["//visibility:public"],
+)
diff --git a/personal/q3k/minecraft/plugin/hscloud/proto/hscloud.proto b/personal/q3k/minecraft/plugin/hscloud/proto/hscloud.proto
new file mode 100644
index 0000000..a578e23
--- /dev/null
+++ b/personal/q3k/minecraft/plugin/hscloud/proto/hscloud.proto
@@ -0,0 +1,22 @@
+syntax = "proto3";
+package hscloud.personal.q3k.minecraft.plugin.hscloud;
+option java_package = "hscloud.personal.q3k.minecraft.plugin.hscloud.proto";
+option java_outer_classname = "Proto";
+option go_package = "hscloud";
+
+service Introspector {
+    rpc Status(StatusRequest) returns (StatusResponse);
+}
+
+message StatusRequest {
+}
+
+message StatusResponse {
+    repeated Player players = 1;
+    int64 time = 2;
+}
+
+message Player {
+    string username = 1;
+    string uuid = 2;
+}
diff --git a/personal/q3k/minecraft/prod.jsonnet b/personal/q3k/minecraft/prod.jsonnet
new file mode 100644
index 0000000..7c7b0ae
--- /dev/null
+++ b/personal/q3k/minecraft/prod.jsonnet
@@ -0,0 +1,217 @@
+local kube = import "../../../kube/kube.libsonnet";
+local defaultWorldguardConfig = import "worldguard.libsonnet";
+
+{
+    local minecraft = self,
+    versions:: {
+        "spigot-1.16.1": "registry.k0.hswaw.net/q3k/minecraft:spigot-1.16.1-r2",
+        "paper-1.16.1": "registry.k0.hswaw.net/q3k/minecraft:paper-1.16.1-r2",
+    },
+    server(name, version):: {
+        local server = self,
+        name:: name,
+        version:: version,
+        image:: minecraft.versions[server.version],
+
+        metadata:: {
+            namespace: "minecraft",
+        },
+
+        componentName(component):: "%s-%s" % [server.name, component],
+
+        properties:: {
+            "spawn-protection": 16,
+            "max-tick-time": 60000,
+            "query.port": 25565,
+            "generator-settings": "",
+            "force-gamemode": false,
+            "allow-nether": true,
+            "enforce-whitelist": false,
+            "gamemode": "survival",
+            "broadcast-console-to-ops": true,
+            "enable-query": false,
+            "player-idle-timeout": 0,
+            "difficulty": "easy",
+            "broadcast-rcon-to-ops": true,
+            "spawn-monsters": true,
+            "op-permission-level": 4,
+            "pvp": true,
+            "snooper-enabled": true,
+            "level-type": "default",
+            "hardcore": false,
+            "enable-command-block": false,
+            "network-compression-threshold": 256,
+            "max-players": 20,
+            "max-world-size": 29999984,
+            "resource-pack-sha1": "",
+            "function-permission-level": 2,
+            "rcon.port": 25575,
+            "server-port": 25565,
+            "debug": false,
+            "server-ip": "",
+            "spawn-npcs": true,
+            "allow-flight": false,
+            "level-name": "world",
+            "view-distance": 10,
+            "resource-pack": "",
+            "spawn-animals": true,
+            "white-list": false,
+            "rcon.password": "",
+            "generate-structures": true,
+            "online-mode": true,
+            "max-build-height": 256,
+            "level-seed": "",
+            "prevent-proxy-connections": false,
+            "use-native-transport": true,
+            "motd": "A Minecraft Server",
+            "enable-rcon": false,
+        },
+
+        worldguardConfig:: defaultWorldguardConfig,
+
+        startsh:: |||
+            #!/usr/bin/env bash
+            cd /home/minecraft/world
+            cp /home/minecraft/config/server.properties .
+            cp /home/minecraft/server.jar .
+            mkdir -p plugins/WorldGuard
+            cp /home/minecraft/worldedit-*.jar plugins
+            cp /home/minecraft/worldguard-*.jar plugins
+            cp /home/minecraft/config/worldguard_config.yaml plugins/WorldGuard/config.yml
+            echo "eula=true" > eula.txt
+
+            bash /home/minecraft/config/overviewer.sh &
+            exec java -Xmx4G -Xms4G -jar server.jar
+        |||,
+
+        overviewersh:: |||
+            #!/usr/bin/env bash
+            mkdir -p overviewer/world
+            sleep 60
+            while true; do
+                echo "Rendering Overviewer map..."
+                overviewer.py -p 1 --rendermodes=normal,lighting,smooth-lighting,cave world overviewer/world
+                echo "Done."
+                sleep 900
+            done
+        |||,
+
+        secretProperties: kube.Secret(server.componentName("properties")) {
+            metadata+: server.metadata,
+            data: {
+                local properties = std.join("\n", ["%s=%s" % [k, std.toString(server.properties[k])] for k in std.objectFields(server.properties)]),
+                "server.properties": std.base64(properties),
+                local worldguardConfig = std.manifestYamlDoc(server.worldguardConfig),
+                "worldguard_config.yaml": std.base64(worldguardConfig),
+                "start.sh": std.base64(server.startsh),
+                "overviewer.sh": std.base64(server.overviewersh),
+            },
+        },
+
+        worldVolume: kube.PersistentVolumeClaim(server.componentName("world")) {
+            metadata+: server.metadata,
+            spec+: {
+                storageClassName: "waw-hdd-redundant-3",
+                accessModes: ["ReadWriteOnce"],
+                resources: {
+                    requests: {
+                        storage: "10Gi",
+                    },
+                },
+            },
+        },
+
+        deploy: kube.Deployment(server.componentName("deploy")) {
+            metadata+: server.metadata,
+            spec+: {
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.SecretVolume(server.secretProperties),
+                            world: kube.PersistentVolumeClaimVolume(server.worldVolume),
+                        },
+                        containers_: {
+                            default: kube.Container("default") {
+                                image: server.image,
+                                command: [
+                                    "bash", "/home/minecraft/config/start.sh",
+                                ],
+                                volumeMounts_: {
+                                    config: { mountPath: "/home/minecraft/config" },
+                                    world: { mountPath: "/home/minecraft/world" },
+                                },
+                                ports_: {
+                                    minecraft: { containerPort: 25565 },
+                                    grpc: { containerPort: 2137 },
+                                },
+                                resources: {
+                                    requests: {
+                                        memory: "6G",
+                                        cpu: "2",
+                                    },
+                                    limits: {
+                                        memory: "6G",
+                                        cpu: "4",
+                                    },
+                                },
+                            },
+                            overviewer: kube.Container("overviewer") {
+                                image: "halverneus/static-file-server:v1.8.0",
+                                env_: {
+                                    FOLDER: "/home/minecraft/world/overviewer/world",
+                                },
+                                volumeMounts_: {
+                                    world: { mountPath: "/home/minecraft/world" },
+                                },
+                                ports_: {
+                                    web: { containerPort: 8080 },
+                                },
+                            },
+                            bridge: kube.Container("bridge") {
+                                image: "registry.k0.hswaw.net/q3k/minecraft-hscloud-bridge:20200518c",
+                                command: [
+                                    "/personal/q3k/minecraft/plugin/hscloud/bridge/bridge",
+                                    "-plugin", "127.0.0.1:2137",
+                                ],
+                                ports_: {
+                                    bridge: { containerPort: 8081 },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+
+        svc: kube.Service(server.componentName("svc")) {
+            metadata+: server.metadata,
+            target_pod:: server.deploy.spec.template,
+            spec+: {
+                ports: [
+                    { name: "minecraft", port: 25565, targetPort: 25565, protocol: "TCP" },
+                    { name: "web", port: 80, targetPort: 8080, protocol: "TCP" },
+                    { name: "bridge", port: 8080, targetPort: 8081, protocol: "TCP" },
+                    { name: "grpc", port: 2137, targetPort: 2137, protocol: "TCP" },
+                ],
+                type: "LoadBalancer",
+                externalTrafficPolicy: "Local",
+            },
+        },
+    },
+
+    ns: kube.Namespace("minecraft"),
+
+    q3k: {
+        survival: minecraft.server("q3k-survival", "paper-1.16.1") {
+            properties+: {
+                motd: "wypierdol z polski kropka pe el",
+                "enforce-whitelist": true,
+            },
+            worldguardConfig+: {
+                mobs+: {
+                    "block-creeper-block-damage": true,
+                },
+            },
+        },
+    },
+}
diff --git a/personal/q3k/minecraft/worldedit-bukkit-7.1.0.jar b/personal/q3k/minecraft/worldedit-bukkit-7.1.0.jar
new file mode 100644
index 0000000..2f3a1e1
--- /dev/null
+++ b/personal/q3k/minecraft/worldedit-bukkit-7.1.0.jar
Binary files differ
diff --git a/personal/q3k/minecraft/worldedit-bukkit-7.2.0-beta-01.jar b/personal/q3k/minecraft/worldedit-bukkit-7.2.0-beta-01.jar
new file mode 100644
index 0000000..93b623a
--- /dev/null
+++ b/personal/q3k/minecraft/worldedit-bukkit-7.2.0-beta-01.jar
Binary files differ
diff --git a/personal/q3k/minecraft/worldguard-bukkit-7.0.2.jar b/personal/q3k/minecraft/worldguard-bukkit-7.0.2.jar
new file mode 100644
index 0000000..1bcc848
--- /dev/null
+++ b/personal/q3k/minecraft/worldguard-bukkit-7.0.2.jar
Binary files differ
diff --git a/personal/q3k/minecraft/worldguard-bukkit-7.0.4-beta1.jar b/personal/q3k/minecraft/worldguard-bukkit-7.0.4-beta1.jar
new file mode 100644
index 0000000..0c071a5
--- /dev/null
+++ b/personal/q3k/minecraft/worldguard-bukkit-7.0.4-beta1.jar
Binary files differ
diff --git a/personal/q3k/minecraft/worldguard.libsonnet b/personal/q3k/minecraft/worldguard.libsonnet
new file mode 100644
index 0000000..0755fcb
--- /dev/null
+++ b/personal/q3k/minecraft/worldguard.libsonnet
@@ -0,0 +1,190 @@
+{
+    regions: {
+        "uuid-migration": {
+            "perform-on-next-start": false,
+            "keep-names-that-lack-uuids": true,
+        },
+        "use-creature-spawn-event": true,
+        sql: {
+            use: false,
+            dsn: "jdbc:mysql://localhost/worldguard",
+            username: "worldguard",
+            password: "worldguard",
+            "table-prefix": "",
+        },
+        "use-paper-entity-origin": false,
+        enable: true,
+        "invincibility-removes-mobs": false,
+        "cancel-chat-without-recipients": true,
+        "nether-portal-protection": false,
+        "fake-player-build-override": true,
+        "explosion-flags-block-entity-damage": true,
+        "high-frequency-flags": false,
+        "protect-against-liquid-flow": false,
+        wand: "minecraft:leather",
+        "max-claim-volume": 30000,
+        "claim-only-inside-existing-regions": false,
+        "location-flags-only-inside-regions": false,
+        "max-region-count-per-player": {
+            default: 7,
+        },
+    },
+    "auto-invincible": false,
+    "auto-invincible-group": false,
+    "auto-no-drowning-group": false,
+    "use-player-move-event": true,
+    "use-player-teleports": true,
+    "use-particle-effects": true,
+    security: {
+        "deop-everyone-on-join": false,
+        "block-in-game-op-command": false,
+        "host-keys-allow-forge-clients": false,
+    },
+    "host-keys": {},
+    "summary-on-start": true,
+    "op-permissions": true,
+    "build-permission-nodes": {
+        enable: false,
+        "deny-message": "&eSorry, but you are not permitted to do that here.",
+    },
+    "event-handling": {
+        "block-entity-spawns-with-untraceable-cause": false,
+        "interaction-whitelist": [],
+        "emit-block-use-at-feet": [],
+        "ignore-hopper-item-move-events": false,
+    },
+    protection: {
+        "item-durability": true,
+        "remove-infinite-stacks": false,
+        "disable-xp-orb-drops": false,
+        "disable-obsidian-generators": false,
+    },
+    gameplay: {
+        "block-potions": [],
+        "block-potions-overly-reliably": false,
+        "disable-conduit-effects": false,
+    },
+    simulation: {
+        sponge: {
+            enable: false,
+            radius: 3,
+            redstone: false,
+        },
+    },
+    default: {
+        "pumpkin-scuba": false,
+        "disable-health-regain": false,
+    },
+    physics: {
+        "no-physics-gravel": false,
+        "no-physics-sand": false,
+        "vine-like-rope-ladders": false,
+        "allow-portal-anywhere": false,
+        "disable-water-damage-blocks": [],
+    },
+    ignition: {
+        "block-tnt": false,
+        "block-tnt-block-damage": false,
+        "block-lighter": false,
+    },
+    fire: {
+        "disable-lava-fire-spread": false,
+        "disable-all-fire-spread": false,
+        "disable-fire-spread-blocks": [],
+        "lava-spread-blocks": [],
+    },
+    mobs: {
+        "block-creeper-explosions": false,
+        "block-creeper-block-damage": false,
+        "block-wither-explosions": false,
+        "block-wither-block-damage": false,
+        "block-wither-skull-explosions": false,
+        "block-wither-skull-block-damage": false,
+        "block-enderdragon-block-damage": false,
+        "block-enderdragon-portal-creation": false,
+        "block-fireball-explosions": false,
+        "block-fireball-block-damage": false,
+        "anti-wolf-dumbness": false,
+        "allow-tamed-spawns": true,
+        "disable-enderman-griefing": false,
+        "disable-snowman-trails": false,
+        "block-painting-destroy": false,
+        "block-item-frame-destroy": false,
+        "block-armor-stand-destroy": false,
+        "block-plugin-spawning": true,
+        "block-above-ground-slimes": false,
+        "block-other-explosions": false,
+        "block-zombie-door-destruction": false,
+        "block-creature-spawn": [],
+    },
+    "player-damage": {
+        "disable-fall-damage": false,
+        "disable-lava-damage": false,
+        "disable-fire-damage": false,
+        "disable-lightning-damage": false,
+        "disable-drowning-damage": false,
+        "disable-suffocation-damage": false,
+        "disable-contact-damage": false,
+        "teleport-on-suffocation": false,
+        "disable-void-damage": false,
+        "teleport-on-void-falling": false,
+        "reset-fall-on-void-teleport": false,
+        "disable-explosion-damage": false,
+        "disable-mob-damage": false,
+        "disable-death-messages": false,
+    },
+    "chest-protection": {
+        enable: false,
+        "disable-off-check": false,
+    },
+    crops: {
+        "disable-creature-trampling": false,
+        "disable-player-trampling": false,
+    },
+    weather: {
+        "prevent-lightning-strike-blocks": [],
+        "disable-lightning-strike-fire": false,
+        "disable-thunderstorm": false,
+        "disable-weather": false,
+        "disable-pig-zombification": false,
+        "disable-villager-witchification": false,
+        "disable-powered-creepers": false,
+        "always-raining": false,
+        "always-thundering": false,
+    },
+    dynamics: {
+        "disable-mushroom-spread": false,
+        "disable-ice-melting": false,
+        "disable-snow-melting": false,
+        "disable-snow-formation": false,
+        "disable-ice-formation": false,
+        "disable-leaf-decay": false,
+        "disable-grass-growth": false,
+        "disable-mycelium-spread": false,
+        "disable-vine-growth": false,
+        "disable-crop-growth": false,
+        "disable-soil-dehydration": false,
+        "snow-fall-blocks": [],
+    },
+    blacklist: {
+        "use-as-whitelist": false,
+        logging: {
+            console: {
+                enable: true,
+            },
+            database: {
+                enable: false,
+                dsn: "jdbc:mysql://localhost:3306/minecraft",
+                user: "root",
+                pass: "",
+                table: "blacklist_events",
+            },
+            file: {
+                enable: false,
+                path: "worldguard/logs/%Y-%m-%d.log",
+                "open-files": 10,
+            },
+        },
+    },
+    "custom-metrics-charts": true,
+}
diff --git a/personal/vuko/shells/README.rst b/personal/vuko/shells/README.rst
new file mode 100644
index 0000000..5e81d45
--- /dev/null
+++ b/personal/vuko/shells/README.rst
@@ -0,0 +1,9 @@
+Hosting for Hackerspace Three Shell System announcement. Currently uploading
+is performed using sftp.
+
+.. code::bash
+    scp index.html shells@185.236.240.58:index.html
+
+TODO:
+    * web interface for shells rotation
+    * access for other members?
diff --git a/personal/vuko/shells/create-secrets.py b/personal/vuko/shells/create-secrets.py
new file mode 100644
index 0000000..7d5df82
--- /dev/null
+++ b/personal/vuko/shells/create-secrets.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+""" generate ssh keys for shells SFTP container """
+from pathlib import Path
+from subprocess import run
+import json
+import tempfile
+
+with tempfile.TemporaryDirectory() as tmp:
+    tmp = Path(tmp).absolute()
+    keyfile = tmp.joinpath("ssh_host_ed25519_key")
+    run(["ssh-keygen", "-f", keyfile, "-N", "", "-t", "ed25519"], check=True)
+
+    # https://kubernetes.io/docs/concepts/configuration/secret/#generating-a-secret-from-files
+    generator = {
+        "secretGenerator": [
+            {
+                "name": "shells-ssh-host-key",
+                "files": [
+                    str(f.relative_to(tmp))
+                    for f in [keyfile, keyfile.with_suffix(".pub")]
+                ],
+            }
+        ]
+    }
+    tmp.joinpath("kustomization.yaml").write_text(json.dumps(generator))
+    run(["kubectl", "-n", "personal-vuko", "apply", "-k", tmp], check=True)
diff --git a/personal/vuko/shells/prod.jsonnet b/personal/vuko/shells/prod.jsonnet
new file mode 100644
index 0000000..463087e
--- /dev/null
+++ b/personal/vuko/shells/prod.jsonnet
@@ -0,0 +1,163 @@
+# this is libjsonnet library for kubernetes related things
+local kube = import '../../../kube/kube.libsonnet';
+
+{
+    local shells = self,
+    local cfg = shells.cfg,
+
+    # namespace defining parameters used by other functions
+    # double colon "::" prevents it from appearing in output file
+    cfg:: {
+        namespace: "personal-vuko",
+        appName: "three-shell-system",
+        domain: "shells.vuko.pl",
+
+        nginx_tag: "latest",
+        nginx_image: "nginxinc/nginx-unprivileged:stable-alpine",
+
+        storageClassName: "waw-hdd-redundant-2",
+
+        resources: {
+            requests: {
+                cpu: "25m",
+                memory: "50Mi",
+            },
+            limits: {
+                cpu: "100m",
+                memory: "200Mi",
+            },
+        },
+    },
+
+    # kubernete namespace personal-${name} for personal usage
+    namespace: kube.Namespace(cfg.namespace),
+
+    # function used for configuring components metatada
+    metadata(component):: {
+        namespace: cfg.namespace,
+        labels: {
+            "app.kubernetes.io/name": cfg.appName,
+            "app.kubernetes.io/managed-by": "kubecfg",
+            "app.kubernetes.io/component": component,
+        },
+    },
+    
+    # component - persistant (non volatile) memory
+    # https://kubernetes.io/docs/concepts/storage/persistent-volumes/
+    dataVolume: kube.PersistentVolumeClaim("html-data") {
+        # override default PersistentVolumeClaim metatada with values defined
+        # in medadata function prevoiusly created
+        # "+" sign before means override
+        metadata+: shells.metadata("html-data"),
+        spec+: {
+            storageClassName: cfg.storageClassName,
+            # can be connected to multiple containers
+            accessModes: [ "ReadWriteMany" ],
+            resources: {
+                requests: {
+                    # amount of storage space: 500Mb
+                    storage: "500Mi",
+                },
+            },
+        },
+    },
+
+    # deployment declares pods
+    # https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
+    deployment: kube.Deployment("shells") {
+        metadata+: shells.metadata("shells"),
+        spec+: {
+            replicas: 1,
+            template+: {
+                spec+: {
+                    # names ending with _ have special meaning in this context
+                    # this is specified in ../../../kube/kube.upstream.jsonnet
+                    # volumes_ { key: { ... } } is converted to volumes [{ name: key, ... }]
+                    volumes_: {
+                        # sftp container host keys secrets saved to kubernetes semi-manually using create-secrets.py
+                        # https://kubernetes.io/docs/concepts/configuration/secret/
+                        host_keys: { secret: { secretName: "shells-ssh-host-key-bd65mg4gbt" } },
+                        # sftp container authorized_keys saved to kubernetes using command:
+                        # kubectl -n personal-vuko create secret generic shells-ssh-authorized-keys --from-file="authorized_keys=${HOME}/.ssh/id_ed25519.pub"
+                        authorized_keys: { secret: { secretName: "shells-ssh-authorized-keys", defaultMode: 256 } },
+                        # to use created volume in deployment we need to claim it
+                        html: kube.PersistentVolumeClaimVolume(shells.dataVolume),
+                    },
+                    # here are containers defined
+                    # when they are defined in one deployment 
+                    containers_: {
+                        shells: kube.Container("nginx") {
+                            image: cfg.nginx_image,
+                            ports_: {
+                                http: { containerPort: 80 },
+                            },
+                            resources: cfg.resources,
+                            volumeMounts_: {
+                                html: { mountPath: "/usr/share/nginx/html" },
+                            },
+                        },
+                        sftp: kube.Container("sftp") {
+                            image: "registry.k0.hswaw.net/vuko/hs-shells-sftp:latest",
+                            ports_: {
+                                sftp: { containerPort: 2222 },
+                            },
+                            command: [ "/bin/start" ],
+                            resources: cfg.resources,
+                            securityContext: {
+                                # specify uid of user running command
+                                runAsUser: 1,
+                            },
+                            volumeMounts_: {
+                                # here volumes defined in volumes_ can be mounted
+                                host_keys: { mountPath: "/etc/ssh/host" },
+                                authorized_keys: { mountPath: "/etc/ssh/auth" },
+                                html: { mountPath: "/data" },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+    },
+
+    # defining a service of type LoadBancer gives you acces from internet
+    # run: kubectl -n personal-${user} get services to see ip address
+    svc: kube.Service("shells") {
+        metadata+: shells.metadata("shells"),
+        target_pod:: shells.deployment.spec.template,
+        spec+: {
+            ports: [
+                { name: "http", port: 80, targetPort: 8080, protocol: "TCP" },
+                { name: "sftp", port: 22, targetPort: 2222, protocol: "TCP" },
+            ],
+            type: "LoadBalancer",
+            externalTrafficPolicy: "Local",
+        },
+    },
+
+    # ingress creates VirtualHost on ingress.k0.hswaw.net forwaring http(s)
+    # requests to your domain to specified Pod/container
+    ingress: kube.Ingress("frontend") {
+        metadata+: shells.metadata("frontend") {
+            annotations+: {
+                "kubernetes.io/tls-acme": "true",
+                "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
+            },
+        },
+        spec+: {
+            tls: [
+                { hosts: [cfg.domain], secretName: "shells-frontend-tls"}
+            ],
+            rules: [
+                {
+                    host: cfg.domain,
+                    http: {
+                        paths: [
+                            { path: "/", backend: shells.svc.name_port },
+                        ],
+                    },
+                },
+            ],
+        },
+    },
+}
diff --git a/personal/vuko/shells/sftp.nix b/personal/vuko/shells/sftp.nix
new file mode 100644
index 0000000..706dc47
--- /dev/null
+++ b/personal/vuko/shells/sftp.nix
@@ -0,0 +1,75 @@
+{ pkgs ? import <nixpkgs> {} }:
+let
+  #dockertarpusher = pkgs.python37Packages.buildPythonPackage {
+  #  pname = "dockertarpusher";
+  #  version = "0.16";
+  #  src = pkgs.fetchFromGitHub {
+  #    owner = "Razikus";
+  #    repo = "dockerregistrypusher";
+  #    rev = "217894b79181a9a02ebc6744e0628777a0f89c36";
+  #    sha256 = "09cqzd9gz42xw30x1jp9mx056k25i20kjzzdg3bk78a4bis29kd4";
+  #  };
+  #  propagatedBuildInputs = with pkgs; [
+  #    python37Packages.requests
+  #  ];
+  #};
+  #hsregistry_push = import ./registrypush {};
+  config = pkgs.runCommand "sshd_config" {} ''
+    mkdir -p $out/etc/ssh/
+    cp ${./sshd_config} $out/etc/ssh/sshd_config
+    #cp ${./test_keys/test_host_key} $out/etc/ssh/ssh_host_ed25519_key
+    #cp ${./test_keys/test_host_key.pub} $out/etc/ssh/ssh_host_ed25519_key.pub
+    #cp ${./test_keys/authorized_keys} $out/etc/ssh/authorized_keys
+  '';
+  name = "vuko/hs-shells-sftp";
+  base = pkgs.dockerTools.buildImage {
+    name = "vuko/ssh-base";
+    tag = "latest";
+    contents = [pkgs.openssh pkgs.busybox];
+  };
+  image = pkgs.dockerTools.buildImage {
+    inherit name;
+    tag = "latest";
+    fromImage = base;
+    contents = [config];
+  
+    runAsRoot = ''
+      #!${pkgs.runtimeShell}
+      mkdir /data/
+      #echo "root:x:0:0::/root:/bin/nologin" > /etc/passwd
+      echo "shells:x:1:1::/data:/bin/sh" >> /etc/passwd
+      mkdir -p /etc/ssh/host/
+      mkdir -p /etc/ssh/auth/
+      mkdir -m 700 /tmp
+      chown 1:1 /tmp
+      
+      cat <<EOF > /bin/start
+      #!/bin/sh
+      cp /etc/ssh/auth/authorized_keys /tmp/authorized_keys
+      /bin/sshd -D -e -f /etc/ssh/sshd_config
+      EOF
+      chmod +x /bin/start
+    '';
+  
+    #https://serverfault.com/questions/344295/is-it-possible-to-run-sshd-as-a-normal-user
+    config = { 
+      Cmd = [ "/bin/start" ];
+      WorkingDir = "/";
+      ExposedPorts =  {
+        "2222/tcp" = {};
+      };
+    };
+  };
+  push = pkgs.writeShellScriptBin "push" ''
+    BASEDIR=$(realpath $(dirname ''${BASH_SOURCE}))
+    docker load < "''${BASEDIR}/../images/sftp.tar.gz"
+    docker tag ${name}:latest registry.k0.hswaw.net/${name}
+    docker push registry.k0.hswaw.net/${name}
+    #exec {hsregistry_push}/bin/hsregistry-push "$BASEDIR/../images/sftp.tar.gz" "$@"
+  '';
+in pkgs.runCommand "hs-shells-sftp" {} ''
+  mkdir $out
+  mkdir -p $out/images $out/bin
+  ln -s ${image} $out/images/sftp.tar.gz
+  install ${push}/bin/push $out/bin/
+''
diff --git a/personal/vuko/shells/sshd_config b/personal/vuko/shells/sshd_config
new file mode 100644
index 0000000..ac4a9ba
--- /dev/null
+++ b/personal/vuko/shells/sshd_config
@@ -0,0 +1,17 @@
+Port 2222
+AddressFamily any
+ListenAddress 0.0.0.0
+#ListenAddress ::
+#UsePrivilegeSeparation no
+UsePAM no
+PermitEmptyPasswords no
+PasswordAuthentication no
+AuthorizedKeysFile /tmp/authorized_keys
+HostKey /etc/ssh/host/ssh_host_ed25519_key
+Subsystem sftp /libexec/sftp-server
+PidFile /tmp/sshd.pid
+
+#ForceCommand internal-sftp
+AllowTcpForwarding no
+X11Forwarding no
+PasswordAuthentication no
diff --git a/third_party/factorio/BUILD b/third_party/factorio/BUILD
new file mode 100644
index 0000000..1028dc0
--- /dev/null
+++ b/third_party/factorio/BUILD
@@ -0,0 +1,3 @@
+load("//third_party/factorio:factorio.bzl", "factorio_images")
+
+factorio_images()
diff --git a/third_party/factorio/README.md b/third_party/factorio/README.md
new file mode 100644
index 0000000..22c7392
--- /dev/null
+++ b/third_party/factorio/README.md
@@ -0,0 +1,6 @@
+Factorio
+========
+
+We allow importing [Factorio](https://factorio.com/) server binaries into hscloud. A machinery is provided that builds Docker images for a given version of factorio and pushes them to `registry.k0.hswaw.net/app/factorio:${version}-${revision}`. Available versions are defined in [factorio.bzl](factorio.bzl).
+
+Production deployments of Factorio are still experimental, see [personal/q3k/factorio](/personal/q3k/factorio).
diff --git a/personal/q3k/factorio/entrypoint.sh b/third_party/factorio/entrypoint.sh
similarity index 100%
rename from personal/q3k/factorio/entrypoint.sh
rename to third_party/factorio/entrypoint.sh
diff --git a/third_party/factorio/factorio.bzl b/third_party/factorio/factorio.bzl
new file mode 100644
index 0000000..6686392
--- /dev/null
+++ b/third_party/factorio/factorio.bzl
@@ -0,0 +1,83 @@
+"""
+Automatically download, package, containerize and push Factorio server images.
+
+For each version defined, the following will be declared:
+  - @factorio-headless-${version}: a repository containing the factorio server tarball
+  - //third_party/factorio:${version}-1: a container_image rule to build a docker container running a factorio server
+  - //third_party/factorio:push_${version}-1: a container_push rule to push that container image to registry.k0.hswaw.net/app/factorio:${version}-1
+
+In addition, a //third_party/factorio:push_latest rule will also be created for the highest versioned server version. This is for convenience.
+
+To add a new version of Factorio, just update the _versions map with the new version number and the sha256 sum of its tarball.
+"""
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
+load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")
+
+# version -> sha256 of server tarball
+_versions = {
+    "0.16.51": "6cb09f5ac87f16f8d5b43cef26c0ae26cc46a57a0382e253dfda032dc5bb367f",
+    "0.17.41": "bf2d16b23c3bbd97e41889d3e27670b6d958fa3d50f0befb41d234f735e8e6d1",
+    "0.17.52": "24458a4e16875b0b63677b7e7a068ce2e5b298c110381d17c6f596fd1406db0e",
+    "0.17.79": "9ace12fa986df028dc1851bf4de2cb038044d743e98823bc1c48ba21aa4d23df",
+    "0.18.12": "e0c6a46d66cfc02cba294a5fd34265e7e7a5168b8c8a7b16ad8dbac31470ed33",
+    "0.18.17": "42adce9fddde393023afb0aae19dd030a32ca0810191c0e7b9b7c55556e9bbce",
+    "0.18.22": "d90e349b61182c1e48bd34797faedc2f9b5b4e349d218ef3d987ae9d90762f7f",
+    "0.18.40": "696fe660fea945f38d12d49cf0b4737522d061fab5b3afd59467c4b2e375711a",
+    "1.0.0": "81d9e1aa94435aeec4131c8869fa6e9331726bea1ea31db750b65ba42dbd1464",
+}
+
+def factorio_repository(version, sha256):
+    http_file(
+        name = "factorio-headless-%s" % (version),
+        urls = [
+            "https://factorio.com/get-download/%s/headless/linux64" % (version),
+        ],
+        sha256 = sha256,
+        downloaded_file_path = "factorio.tar.xz",
+    )
+
+def factorio_repositories():
+    for v, sha256 in _versions.items():
+        factorio_repository(v, sha256)
+
+def factorio_image(version, revision):
+    image_name = "%s-%d" % (version, revision)
+    container_image(
+        name = image_name,
+        base = "@prodimage-bionic//image",
+        tars = ["@factorio-headless-%s//file" % (version)],
+        files = [":entrypoint.sh"],
+        directory = "/",
+        entrypoint = ["/entrypoint.sh"],
+    )
+    container_push(
+        name = "push_%s-%d" % (version, revision),
+        image = ":%s" % (image_name),
+        format = "Docker",
+        registry = "registry.k0.hswaw.net",
+        repository = "app/factorio",
+        tag = "%s-%d" % (version, revision),
+    )
+
+def _sort_by_version(v):
+    return [int(el) for el in v.split(".")]
+
+def factorio_images():
+    revision_overrides = {
+        "0.18.12": 2,
+    }
+    for v in _versions.keys():
+        revision = revision_overrides.get(v, 1)
+        factorio_image(v, revision)
+
+    highest_version = sorted(_versions.keys(), key=_sort_by_version, reverse=True)[0]
+    revision = revision_overrides.get(highest_version, 1)
+    container_push(
+        name = "push_latest",
+        image = ":%s-%d" % (highest_version, revision),
+        format = "Docker",
+        registry = "registry.k0.hswaw.net",
+        repository = "q3k/factorio",
+        tag = "%s-%d" % (highest_version, revision),
+    )
diff --git a/third_party/go/BUILD b/third_party/go/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/go/BUILD
diff --git a/third_party/go/repositories.bzl b/third_party/go/repositories.bzl
new file mode 100644
index 0000000..0a3ef7c
--- /dev/null
+++ b/third_party/go/repositories.bzl
@@ -0,0 +1,1719 @@
+load("@bazel_gazelle//:deps.bzl", "go_repository")
+
+def go_repositories():
+    go_repository(
+        name = "org_golang_x_net",
+        commit = "d3edc9973b7eb1fb302b0ff2c62357091cea9a30",
+        importpath = "golang.org/x/net",
+    )
+
+    go_repository(
+        name = "io_k8s_kubernetes",
+        importpath = "k8s.io/kubernetes",
+        # Get from HTTP instead, this repository is _big_
+        urls = ["https://github.com/kubernetes/kubernetes/archive/v1.16.0.tar.gz"],
+        sha256 = "a8b2ee84ce38fa14404d7e56daa87aa2f2fb13e0114fb1150f294c992ab3f36c",
+        strip_prefix = "kubernetes-1.16.0",
+    )
+
+    go_repository(
+        name = "io_k8s_repo_infra",
+        commit = "df02ded38f9506e5bbcbf21702034b4fef815f2f",
+        importpath = "k8s.io/repo-infra",
+    )
+
+    go_repository(
+        name = "com_github_bitnami_kubecfg",
+        importpath = "github.com/bitnami/kubecfg",
+        vcs = "git",
+        commit = "dddf366990f581132cd046c723d73a2357de2dc8",
+        remote = "https://github.com/q3k/kubecfg",
+    )
+
+    go_repository(
+        name = "com_github_projectcalico_calicoctl",
+        importpath = "github.com/projectcalico/calicoctl",
+        # This fork implements explicit Bazel rules
+        remote = "https://github.com/q3k/calicoctl",
+        vcs = "git",
+        commit = "1bc31862f07e7539ca493de9137ed1ad56cc9f43",
+        build_file_generation = "off",
+    )
+
+    go_repository(
+        name = "com_github_shirou_gopsutil",
+        commit = "2cbc9195c892b304060269ef280375236d2fcac9",
+        importpath = "github.com/shirou/gopsutil",
+    )
+
+    go_repository(
+        name = "com_github_cloudflare_cfssl",
+        commit = "768cd563887febaad559b511aaa5964823ccb4ab",
+        importpath = "github.com/cloudflare/cfssl",
+    )
+
+    go_repository(
+        name = "com_github_mattn_go_sqlite3",
+        commit = "5994cc52dfa89a4ee21ac891b06fbc1ea02c52d3",
+        importpath = "github.com/mattn/go-sqlite3",
+    )
+
+    go_repository(
+        name = "com_github_sebastiaanklippert_go_wkhtmltopdf",
+        commit = "72a7793efd38728796273861bb27d590cc33d9d4",
+        importpath = "github.com/sebastiaanklippert/go-wkhtmltopdf",
+    )
+
+    go_repository(
+        name = "com_github_ziutek_telnet",
+        commit = "c3b780dc415b28894076b4ec975ea3ea69e3980f",
+        importpath = "github.com/ziutek/telnet",
+    )
+
+    go_repository(
+        name = "com_github_digitalocean_go_netbox",
+        commit = "29433ec527e78486ea0a5758817ab672d977f90e",
+        importpath = "github.com/digitalocean/go-netbox",
+    )
+
+    go_repository(
+        name = "com_github_cenkalti_backoff",
+        commit = "4b4cebaf850ec58f1bb1fec5bdebdf8501c2bc3f",
+        importpath = "github.com/cenkalti/backoff",
+    )
+
+    go_repository(
+        name = "ml_vbom_util",
+        commit = "db5cfe13f5cc",
+        importpath = "vbom.ml/util",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_strfmt",
+        importpath = "github.com/go-openapi/strfmt",
+        tag = "v0.19.0",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_swag",
+        importpath = "github.com/go-openapi/swag",
+        tag = "v0.19.5",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_errors",
+        importpath = "github.com/go-openapi/errors",
+        tag = "v0.19.2",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_runtime",
+        importpath = "github.com/go-openapi/runtime",
+        tag = "v0.19.0",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_validate",
+        importpath = "github.com/go-openapi/validate",
+        tag = "v0.19.2",
+    )
+
+    go_repository(
+        name = "com_github_mitchellh_mapstructure",
+        importpath = "github.com/mitchellh/mapstructure",
+        tag = "v1.1.2",
+    )
+
+    go_repository(
+        name = "org_mongodb_go_mongo_driver",
+        commit = "9ec4480161a76f5267d56fc836b7f6d357fd9209",
+        importpath = "go.mongodb.org/mongo-driver",
+    )
+
+    go_repository(
+        name = "in_gopkg_yaml_v2",
+        importpath = "gopkg.in/yaml.v2",
+        tag = "v2.2.4",
+    )
+
+    go_repository(
+        name = "com_github_asaskevich_govalidator",
+        commit = "f61b66f89f4a",
+        importpath = "github.com/asaskevich/govalidator",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_spec",
+        importpath = "github.com/go-openapi/spec",
+        tag = "v0.19.2",
+    )
+
+    go_repository(
+        name = "com_github_mailru_easyjson",
+        commit = "b2ccc519800e",
+        importpath = "github.com/mailru/easyjson",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_analysis",
+        importpath = "github.com/go-openapi/analysis",
+        tag = "v0.19.2",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_jsonpointer",
+        importpath = "github.com/go-openapi/jsonpointer",
+        tag = "v0.19.3",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_loads",
+        importpath = "github.com/go-openapi/loads",
+        tag = "v0.19.2",
+    )
+
+    go_repository(
+        name = "com_github_go_openapi_jsonreference",
+        importpath = "github.com/go-openapi/jsonreference",
+        tag = "v0.19.2",
+    )
+
+    go_repository(
+        name = "com_github_puerkitobio_purell",
+        importpath = "github.com/PuerkitoBio/purell",
+        tag = "v1.1.1",
+    )
+
+    go_repository(
+        name = "com_github_puerkitobio_urlesc",
+        commit = "de5bf2ad4578",
+        importpath = "github.com/PuerkitoBio/urlesc",
+    )
+
+    go_repository(
+        name = "com_github_abbot_go_http_auth",
+        commit = "860ed7f246ff5abfdbd5c7ce618fd37b49fd3d86",
+        importpath = "github.com/abbot/go-http-auth",
+    )
+
+    go_repository(
+        name = "com_github_urfave_cli",
+        importpath = "github.com/urfave/cli",
+        tag = "v1.20.0",
+    )
+
+    go_repository(
+        name = "org_golang_x_crypto",
+        commit = "60c769a6c586",
+        importpath = "golang.org/x/crypto",
+    )
+
+    go_repository(
+        name = "org_golang_x_oauth2",
+        commit = "0f29369cfe45",
+        importpath = "golang.org/x/oauth2",
+    )
+
+    go_repository(
+        name = "com_github_djherbis_atime",
+        commit = "2d569978378562c466df74eda2d82900f435c5f4",
+        importpath = "github.com/djherbis/atime",
+    )
+
+    go_repository(
+        name = "com_google_cloud_go",
+        importpath = "cloud.google.com/go",
+        tag = "v0.38.0",
+    )
+
+    go_repository(
+        name = "com_github_stackexchange_wmi",
+        commit = "cbe66965904dbe8a6cd589e2298e5d8b986bd7dd",
+        importpath = "github.com/stackexchange/wmi",
+    )
+
+    go_repository(
+        name = "com_github_go_ole_go_ole",
+        commit = "938323a72016e9cf84fa5fba7635089efb0ad87f",
+        importpath = "github.com/go-ole/go-ole",
+    )
+
+    go_repository(
+        name = "com_github_dustin_go_humanize",
+        importpath = "github.com/dustin/go-humanize",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "io_k8s_client_go",
+        commit = "0a8a1d7b7fae",
+        importpath = "k8s.io/client-go",
+        build_extra_args = [
+            "-known_import=github.com/Azure/go-autorest",
+            "-known_import=github.com/googleapis/gnostic",
+        ],
+    )
+
+    go_repository(
+        name = "io_k8s_apimachinery",
+        build_file_proto_mode = "disable",
+        commit = "731dcecc205498f52a21b12e311af095efb4b188",
+        importpath = "k8s.io/apimachinery",
+        patches = ["//third_party/go/k8s-apimachinery:fix-kubernetes-bug-87675.patch"],
+        patch_args = ["-p1"],
+    )
+
+    go_repository(
+        name = "io_k8s_klog",
+        importpath = "k8s.io/klog",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "io_k8s_utils",
+        commit = "e782cd3c129f",
+        importpath = "k8s.io/utils",
+    )
+
+    go_repository(
+        name = "com_github_googleapis_gnostic",
+        commit = "15cf44e552f9",
+        importpath = "github.com/googleapis/gnostic",
+        build_file_generation = "on",
+        build_file_proto_mode = "disable",
+    )
+
+    go_repository(
+        name = "io_k8s_api",
+        build_file_proto_mode = "disable",
+        commit = "bbc9463b57e5",
+        importpath = "k8s.io/api",
+    )
+
+    go_repository(
+        name = "org_golang_x_time",
+        commit = "9d24e82272b4",
+        importpath = "golang.org/x/time",
+    )
+
+    go_repository(
+        name = "com_github_google_gofuzz",
+        importpath = "github.com/google/gofuzz",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "io_k8s_sigs_yaml",
+        importpath = "sigs.k8s.io/yaml",
+        tag = "v1.1.0",
+    )
+
+    go_repository(
+        name = "com_github_modern_go_reflect2",
+        importpath = "github.com/modern-go/reflect2",
+        tag = "v1.0.1",
+    )
+
+    go_repository(
+        name = "com_github_davecgh_go_spew",
+        importpath = "github.com/davecgh/go-spew",
+        tag = "v1.1.1",
+    )
+
+    go_repository(
+        name = "com_github_json_iterator_go",
+        importpath = "github.com/json-iterator/go",
+        tag = "v1.1.8",
+    )
+
+    go_repository(
+        name = "com_github_modern_go_concurrent",
+        commit = "bacd9c7ef1dd",
+        importpath = "github.com/modern-go/concurrent",
+    )
+
+    go_repository(
+        name = "in_gopkg_inf_v0",
+        importpath = "gopkg.in/inf.v0",
+        tag = "v0.9.1",
+    )
+
+    go_repository(
+        name = "com_github_cloudflare_cfrpki",
+        commit = "adece784464315db69299ba75e9287c60cd95c69",
+        importpath = "github.com/cloudflare/cfrpki",
+    )
+
+    go_repository(
+        name = "com_github_prometheus_client_golang",
+        importpath = "github.com/prometheus/client_golang",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_rs_cors",
+        commit = "db0fe48135e83b5812a5a31be0eea66984b1b521",
+        importpath = "github.com/rs/cors",
+    )
+
+    go_repository(
+        name = "com_github_cloudflare_gortr",
+        commit = "95270606e8853d9b93f5be46d656d08ec0a4ef09",
+        importpath = "github.com/cloudflare/gortr",
+    )
+
+    go_repository(
+        name = "com_github_gorilla_mux",
+        importpath = "github.com/gorilla/mux",
+        tag = "v1.6.2",
+    )
+
+    go_repository(
+        name = "com_github_sirupsen_logrus",
+        importpath = "github.com/sirupsen/logrus",
+        tag = "v1.4.2",
+    )
+
+    go_repository(
+        name = "com_github_prometheus_common",
+        importpath = "github.com/prometheus/common",
+        tag = "v0.4.1",
+    )
+
+    go_repository(
+        name = "com_github_beorn7_perks",
+        importpath = "github.com/beorn7/perks",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_prometheus_client_model",
+        commit = "fd36f4220a90",
+        importpath = "github.com/prometheus/client_model",
+    )
+
+    go_repository(
+        name = "com_github_prometheus_procfs",
+        importpath = "github.com/prometheus/procfs",
+        tag = "v0.0.2",
+    )
+
+    go_repository(
+        name = "com_github_matttproud_golang_protobuf_extensions",
+        importpath = "github.com/matttproud/golang_protobuf_extensions",
+        tag = "v1.0.1",
+    )
+
+    go_repository(
+        name = "com_github_jmoiron_sqlx",
+        commit = "38398a30ed8516ffda617a04c822de09df8a3ec5",
+        importpath = "github.com/jmoiron/sqlx",
+    )
+
+    go_repository(
+        name = "com_github_lib_pq",
+        commit = "3427c32cb71afc948325f299f040e53c1dd78979",
+        importpath = "github.com/lib/pq",
+    )
+
+    go_repository(
+        name = "com_github_gchaincl_sqlhooks",
+        commit = "1932c8dd22f2283687586008bf2d58c2c5c014d0",
+        importpath = "github.com/gchaincl/sqlhooks",
+    )
+
+    go_repository(
+        name = "com_github_golang_migrate_migrate_v4",
+        commit = "e93eaeb3fe21ce2ccc1365277a01863e6bc84d9c",
+        importpath = "github.com/golang-migrate/migrate/v4",
+        remote = "https://github.com/golang-migrate/migrate",
+        vcs = "git",
+    )
+
+    go_repository(
+        name = "com_github_hashicorp_go_multierror",
+        commit = "bdca7bb83f603b80ef756bb953fe1dafa9cd00a2",
+        importpath = "github.com/hashicorp/go-multierror",
+    )
+
+    go_repository(
+        name = "com_github_hashicorp_errwrap",
+        commit = "8a6fb523712970c966eefc6b39ed2c5e74880354",
+        importpath = "github.com/hashicorp/errwrap",
+    )
+
+    go_repository(
+        name = "com_github_cockroachdb_cockroach_go",
+        commit = "e0a95dfd547cc9c3ebaaba1a12c2afe4bf621ac5",
+        importpath = "github.com/cockroachdb/cockroach-go",
+    )
+
+    go_repository(
+        name = "com_github_jackc_pgx",
+        commit = "6954c15ad0bd3c9aa6dd1b190732b020379beb28",
+        importpath = "github.com/jackc/pgx",
+    )
+
+    go_repository(
+        name = "com_github_golang_collections_go_datastructures",
+        commit = "59788d5eb2591d3497ffb8fafed2f16fe00e7775",
+        importpath = "github.com/golang-collections/go-datastructures",
+    )
+
+    go_repository(
+        name = "com_github_go_test_deep",
+        commit = "cf67d735e69b4a4d50cdf571a92b0144786080f7",
+        importpath = "github.com/go-test/deep",
+    )
+
+    go_repository(
+        name = "com_github_sethvargo_go_password",
+        commit = "68ac5879751a7105834296859f8c1bf70b064675",
+        importpath = "github.com/sethvargo/go-password",
+    )
+
+    go_repository(
+        name = "in_gopkg_ldap_v3",
+        commit = "9f0d712775a0973b7824a1585a86a4ea1d5263d9",
+        importpath = "gopkg.in/ldap.v3",
+    )
+
+    go_repository(
+        name = "in_gopkg_asn1_ber_v1",
+        commit = "f715ec2f112d1e4195b827ad68cf44017a3ef2b1",
+        importpath = "gopkg.in/asn1-ber.v1",
+    )
+
+    go_repository(
+        name = "com_github_q3k_cursedjsonrpc",
+        commit = "304f0561c9162a2696f3ae7c96f3404324177ab8",
+        importpath = "github.com/q3k/cursedjsonrpc",
+    )
+
+    go_repository(
+        name = "com_github_q3k_cursedjson",
+        commit = "af0e3abb1bcef7197b3b9f91d7d094e6528a2d05",
+        importpath = "github.com/q3k/cursedjson",
+    )
+
+    go_repository(
+        name = "co_honnef_go_tools",
+        commit = "ea95bdfd59fc",
+        importpath = "honnef.co/go/tools",
+    )
+
+    go_repository(
+        name = "com_github_azure_go_ansiterm",
+        commit = "d6e3b3328b78",
+        importpath = "github.com/Azure/go-ansiterm",
+    )
+
+    go_repository(
+        name = "com_github_azure_go_autorest",
+        importpath = "github.com/Azure/go-autorest",
+        tag = "v11.5.0",
+    )
+
+    native.local_repository(
+        name = "com_github_census_instrumentation_opencensus_proto",
+        path = "./third_party/go/opencensus-proto",
+    )
+
+    go_repository(
+        name = "com_github_client9_misspell",
+        importpath = "github.com/client9/misspell",
+        tag = "v0.3.4",
+    )
+
+    go_repository(
+        name = "com_github_containerd_continuity",
+        commit = "7f53d412b9eb",
+        importpath = "github.com/containerd/continuity",
+    )
+
+    go_repository(
+        name = "com_github_coreos_clair",
+        commit = "44ae4bc9590a",
+        importpath = "github.com/coreos/clair",
+    )
+
+    go_repository(
+        name = "com_github_dgrijalva_jwt_go",
+        importpath = "github.com/dgrijalva/jwt-go",
+        tag = "v3.2.0",
+    )
+
+    go_repository(
+        name = "com_github_docker_cli",
+        commit = "54c19e67f69c",
+        importpath = "github.com/docker/cli",
+    )
+
+    go_repository(
+        name = "com_github_docker_distribution",
+        importpath = "github.com/docker/distribution",
+        tag = "v2.7.1",
+    )
+
+    go_repository(
+        name = "com_github_docker_docker",
+        commit = "be7ac8be2ae0",
+        importpath = "github.com/docker/docker",
+    )
+
+    go_repository(
+        name = "com_github_docker_docker_ce",
+        commit = "f53bd8bb8e43",
+        importpath = "github.com/docker/docker-ce",
+    )
+
+    go_repository(
+        name = "com_github_docker_docker_credential_helpers",
+        importpath = "github.com/docker/docker-credential-helpers",
+        tag = "v0.6.1",
+    )
+
+    go_repository(
+        name = "com_github_docker_go_connections",
+        commit = "97c2040d34df",
+        importpath = "github.com/docker/go-connections",
+    )
+
+    go_repository(
+        name = "com_github_docker_go_metrics",
+        commit = "399ea8c73916",
+        importpath = "github.com/docker/go-metrics",
+    )
+
+    go_repository(
+        name = "com_github_docker_go_units",
+        importpath = "github.com/docker/go-units",
+        tag = "v0.3.3",
+    )
+
+    go_repository(
+        name = "com_github_docker_libtrust",
+        commit = "aabc10ec26b7",
+        importpath = "github.com/docker/libtrust",
+    )
+
+    go_repository(
+        name = "com_github_elazarl_go_bindata_assetfs",
+        commit = "38087fe4dafb",
+        importpath = "github.com/elazarl/go-bindata-assetfs",
+    )
+
+    go_repository(
+        name = "com_github_evanphx_json_patch",
+        importpath = "github.com/evanphx/json-patch",
+        tag = "v4.2.0",
+    )
+
+    go_repository(
+        name = "com_github_fernet_fernet_go",
+        commit = "9eac43b88a5e",
+        importpath = "github.com/fernet/fernet-go",
+    )
+
+    go_repository(
+        name = "com_github_fsnotify_fsnotify",
+        importpath = "github.com/fsnotify/fsnotify",
+        tag = "v1.4.7",
+    )
+
+    go_repository(
+        name = "com_github_genuinetools_pkg",
+        commit = "1c141f661797",
+        importpath = "github.com/genuinetools/pkg",
+    )
+
+    go_repository(
+        name = "com_github_genuinetools_reg",
+        commit = "d959057b30da",
+        importpath = "github.com/genuinetools/reg",
+    )
+
+    go_repository(
+        name = "com_github_ghodss_yaml",
+        importpath = "github.com/ghodss/yaml",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_golang_glog",
+        commit = "23def4e6c14b",
+        importpath = "github.com/golang/glog",
+    )
+
+    go_repository(
+        name = "com_github_golang_mock",
+        importpath = "github.com/golang/mock",
+        tag = "v1.2.0",
+    )
+
+    go_repository(
+        name = "com_github_google_btree",
+        importpath = "github.com/google/btree",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_google_go_cmp",
+        importpath = "github.com/google/go-cmp",
+        tag = "v0.3.0",
+    )
+
+    go_repository(
+        name = "com_github_google_go_jsonnet",
+        importpath = "github.com/google/go-jsonnet",
+        tag = "v0.12.1",
+    )
+
+    go_repository(
+        name = "com_github_gophercloud_gophercloud",
+        importpath = "github.com/gophercloud/gophercloud",
+        tag = "v0.1.0",
+    )
+
+    go_repository(
+        name = "com_github_gorilla_context",
+        importpath = "github.com/gorilla/context",
+        tag = "v1.1.1",
+    )
+
+    go_repository(
+        name = "com_github_gregjones_httpcache",
+        commit = "9cad4c3443a7",
+        importpath = "github.com/gregjones/httpcache",
+    )
+
+    go_repository(
+        name = "com_github_grpc_ecosystem_grpc_gateway",
+        importpath = "github.com/grpc-ecosystem/grpc-gateway",
+        tag = "v1.9.5",
+    )
+
+    go_repository(
+        name = "com_github_hpcloud_tail",
+        importpath = "github.com/hpcloud/tail",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_imdario_mergo",
+        importpath = "github.com/imdario/mergo",
+        tag = "v0.3.5",
+    )
+
+    go_repository(
+        name = "com_github_inconshreveable_mousetrap",
+        importpath = "github.com/inconshreveable/mousetrap",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_kisielk_gotool",
+        importpath = "github.com/kisielk/gotool",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_kr_pretty",
+        importpath = "github.com/kr/pretty",
+        tag = "v0.1.0",
+    )
+
+    go_repository(
+        name = "com_github_kr_pty",
+        importpath = "github.com/kr/pty",
+        tag = "v1.1.5",
+    )
+
+    go_repository(
+        name = "com_github_kr_text",
+        importpath = "github.com/kr/text",
+        tag = "v0.1.0",
+    )
+
+    go_repository(
+        name = "com_github_mattn_go_isatty",
+        importpath = "github.com/mattn/go-isatty",
+        tag = "v0.0.4",
+    )
+
+    go_repository(
+        name = "com_github_microsoft_go_winio",
+        importpath = "github.com/Microsoft/go-winio",
+        tag = "v0.4.11",
+    )
+
+    go_repository(
+        name = "com_github_mitchellh_go_wordwrap",
+        importpath = "github.com/mitchellh/go-wordwrap",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_nvveen_gotty",
+        commit = "cd527374f1e5",
+        importpath = "github.com/Nvveen/Gotty",
+    )
+
+    go_repository(
+        name = "com_github_onsi_ginkgo",
+        importpath = "github.com/onsi/ginkgo",
+        tag = "v1.10.1",
+    )
+
+    go_repository(
+        name = "com_github_onsi_gomega",
+        importpath = "github.com/onsi/gomega",
+        tag = "v1.7.0",
+    )
+
+    go_repository(
+        name = "com_github_opencontainers_go_digest",
+        importpath = "github.com/opencontainers/go-digest",
+        tag = "v1.0.0-rc1",
+    )
+
+    go_repository(
+        name = "com_github_opencontainers_image_spec",
+        importpath = "github.com/opencontainers/image-spec",
+        tag = "v1.0.1",
+    )
+
+    go_repository(
+        name = "com_github_opencontainers_runc",
+        importpath = "github.com/opencontainers/runc",
+        tag = "v0.1.1",
+    )
+
+    go_repository(
+        name = "com_github_openzipkin_zipkin_go",
+        importpath = "github.com/openzipkin/zipkin-go",
+        tag = "v0.1.3",
+    )
+
+    go_repository(
+        name = "com_github_peterbourgon_diskv",
+        importpath = "github.com/peterbourgon/diskv",
+        tag = "v2.0.1",
+    )
+
+    go_repository(
+        name = "com_github_peterhellberg_link",
+        importpath = "github.com/peterhellberg/link",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_pkg_errors",
+        importpath = "github.com/pkg/errors",
+        tag = "v0.8.1",
+    )
+
+    go_repository(
+        name = "com_github_pmezard_go_difflib",
+        importpath = "github.com/pmezard/go-difflib",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_sergi_go_diff",
+        commit = "feef008d51ad",
+        importpath = "github.com/sergi/go-diff",
+    )
+
+    go_repository(
+        name = "com_github_shurcool_httpfs",
+        commit = "809beceb2371",
+        importpath = "github.com/shurcooL/httpfs",
+    )
+
+    go_repository(
+        name = "com_github_spf13_cobra",
+        importpath = "github.com/spf13/cobra",
+        tag = "v0.0.5",
+    )
+
+    go_repository(
+        name = "com_github_spf13_pflag",
+        importpath = "github.com/spf13/pflag",
+        tag = "v1.0.5",
+    )
+
+    go_repository(
+        name = "com_github_stretchr_objx",
+        importpath = "github.com/stretchr/objx",
+        tag = "v0.2.0",
+    )
+
+    go_repository(
+        name = "com_github_stretchr_testify",
+        importpath = "github.com/stretchr/testify",
+        tag = "v1.3.0",
+    )
+
+    go_repository(
+        name = "in_gopkg_airbrake_gobrake_v2",
+        importpath = "gopkg.in/airbrake/gobrake.v2",
+        tag = "v2.0.9",
+    )
+
+    go_repository(
+        name = "in_gopkg_check_v1",
+        commit = "788fd7840127",
+        importpath = "gopkg.in/check.v1",
+    )
+
+    go_repository(
+        name = "in_gopkg_fsnotify_v1",
+        importpath = "gopkg.in/fsnotify.v1",
+        tag = "v1.4.7",
+    )
+
+    go_repository(
+        name = "in_gopkg_gemnasium_logrus_airbrake_hook_v2",
+        importpath = "gopkg.in/gemnasium/logrus-airbrake-hook.v2",
+        tag = "v2.1.2",
+    )
+
+    go_repository(
+        name = "in_gopkg_tomb_v1",
+        commit = "dd632973f1e7",
+        importpath = "gopkg.in/tomb.v1",
+    )
+
+    go_repository(
+        name = "io_k8s_apiextensions_apiserver",
+        build_file_proto_mode = "disable",
+        commit = "b615a37f53e7",
+        importpath = "k8s.io/apiextensions-apiserver",
+    )
+
+    go_repository(
+        name = "io_k8s_kube_openapi",
+        commit = "30be4d16710a",
+        importpath = "k8s.io/kube-openapi",
+        build_extra_args = ["-known_import=github.com/googleapis/gnostic"],
+    )
+
+    go_repository(
+        name = "io_opencensus_go",
+        importpath = "go.opencensus.io",
+        tag = "v0.21.0",
+    )
+
+    go_repository(
+        name = "io_opencensus_go_contrib_exporter_ocagent",
+        importpath = "contrib.go.opencensus.io/exporter/ocagent",
+        tag = "v0.6.0",
+    )
+
+    go_repository(
+        name = "org_apache_git_thrift_git",
+        commit = "9b75e4fe745a",
+        importpath = "git.apache.org/thrift.git",
+    )
+
+    go_repository(
+        name = "org_golang_google_api",
+        importpath = "google.golang.org/api",
+        tag = "v0.4.0",
+    )
+
+    go_repository(
+        name = "org_golang_google_appengine",
+        importpath = "google.golang.org/appengine",
+        tag = "v1.5.0",
+    )
+
+    go_repository(
+        name = "org_golang_google_grpc",
+        importpath = "google.golang.org/grpc",
+        tag = "v1.29.1",
+    )
+
+    go_repository(
+        name = "org_golang_x_lint",
+        commit = "d0100b6bd8b3",
+        importpath = "golang.org/x/lint",
+    )
+
+    go_repository(
+        name = "org_golang_x_sync",
+        commit = "112230192c58",
+        importpath = "golang.org/x/sync",
+    )
+
+    go_repository(
+        name = "org_golang_x_sys",
+        commit = "c7b8b68b1456",
+        importpath = "golang.org/x/sys",
+    )
+
+    go_repository(
+        name = "org_golang_x_text",
+        importpath = "golang.org/x/text",
+        tag = "v0.3.2",
+    )
+
+    go_repository(
+        name = "tools_gotest",
+        importpath = "gotest.tools",
+        tag = "v2.2.0",
+    )
+
+    go_repository(
+        name = "com_github_alecthomas_template",
+        commit = "a0175ee3bccc",
+        importpath = "github.com/alecthomas/template",
+    )
+
+    go_repository(
+        name = "com_github_alecthomas_units",
+        commit = "2efee857e7cf",
+        importpath = "github.com/alecthomas/units",
+    )
+
+    go_repository(
+        name = "com_github_armon_consul_api",
+        commit = "eb2c6b5be1b6",
+        importpath = "github.com/armon/consul-api",
+    )
+
+    go_repository(
+        name = "com_github_bgentry_speakeasy",
+        importpath = "github.com/bgentry/speakeasy",
+        tag = "v0.1.0",
+    )
+
+    go_repository(
+        name = "com_github_blang_semver",
+        importpath = "github.com/blang/semver",
+        tag = "v3.5.0",
+    )
+
+    go_repository(
+        name = "com_github_burntsushi_toml",
+        importpath = "github.com/BurntSushi/toml",
+        tag = "v0.3.1",
+    )
+
+    go_repository(
+        name = "com_github_burntsushi_xgb",
+        commit = "27f122750802",
+        importpath = "github.com/BurntSushi/xgb",
+    )
+
+    go_repository(
+        name = "com_github_chai2010_gettext_go",
+        commit = "c6fed771bfd5",
+        importpath = "github.com/chai2010/gettext-go",
+    )
+
+    go_repository(
+        name = "com_github_cockroachdb_datadriven",
+        commit = "80d97fb3cbaa",
+        importpath = "github.com/cockroachdb/datadriven",
+    )
+
+    go_repository(
+        name = "com_github_coreos_etcd",
+        importpath = "github.com/coreos/etcd",
+        tag = "v3.3.10",
+    )
+
+    go_repository(
+        name = "com_github_coreos_go_etcd",
+        importpath = "github.com/coreos/go-etcd",
+        tag = "v2.0.0",
+    )
+
+    go_repository(
+        name = "com_github_coreos_go_oidc",
+        importpath = "github.com/coreos/go-oidc",
+        tag = "v2.1.0",
+    )
+
+    go_repository(
+        name = "com_github_coreos_go_semver",
+        importpath = "github.com/coreos/go-semver",
+        tag = "v0.3.0",
+    )
+
+    go_repository(
+        name = "com_github_coreos_go_systemd",
+        commit = "95778dfbb74e",
+        importpath = "github.com/coreos/go-systemd",
+    )
+
+    go_repository(
+        name = "com_github_coreos_pkg",
+        commit = "97fdf19511ea",
+        importpath = "github.com/coreos/pkg",
+    )
+
+    go_repository(
+        name = "com_github_cpuguy83_go_md2man",
+        importpath = "github.com/cpuguy83/go-md2man",
+        tag = "v1.0.10",
+    )
+
+    go_repository(
+        name = "com_github_creack_pty",
+        importpath = "github.com/creack/pty",
+        tag = "v1.1.7",
+    )
+
+    go_repository(
+        name = "com_github_daviddengcn_go_colortext",
+        commit = "511bcaf42ccd",
+        importpath = "github.com/daviddengcn/go-colortext",
+    )
+
+    go_repository(
+        name = "com_github_docker_spdystream",
+        commit = "449fdfce4d96",
+        importpath = "github.com/docker/spdystream",
+    )
+
+    go_repository(
+        name = "com_github_elazarl_goproxy",
+        commit = "c4fc26588b6e",
+        importpath = "github.com/elazarl/goproxy",
+    )
+
+    go_repository(
+        name = "com_github_emicklei_go_restful",
+        importpath = "github.com/emicklei/go-restful",
+        tag = "v2.9.5",
+    )
+
+    go_repository(
+        name = "com_github_exponent_io_jsonpath",
+        commit = "d6023ce2651d",
+        importpath = "github.com/exponent-io/jsonpath",
+    )
+
+    go_repository(
+        name = "com_github_fatih_camelcase",
+        importpath = "github.com/fatih/camelcase",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_fatih_color",
+        importpath = "github.com/fatih/color",
+        tag = "v1.7.0",
+    )
+
+    go_repository(
+        name = "com_github_globalsign_mgo",
+        commit = "eeefdecb41b8",
+        importpath = "github.com/globalsign/mgo",
+    )
+
+    go_repository(
+        name = "com_github_go_kit_kit",
+        importpath = "github.com/go-kit/kit",
+        tag = "v0.8.0",
+    )
+
+    go_repository(
+        name = "com_github_go_logfmt_logfmt",
+        importpath = "github.com/go-logfmt/logfmt",
+        tag = "v0.3.0",
+    )
+
+    go_repository(
+        name = "com_github_go_logr_logr",
+        importpath = "github.com/go-logr/logr",
+        tag = "v0.1.0",
+    )
+
+    go_repository(
+        name = "com_github_go_stack_stack",
+        importpath = "github.com/go-stack/stack",
+        tag = "v1.8.0",
+    )
+
+    go_repository(
+        name = "com_github_golang_groupcache",
+        commit = "02826c3e7903",
+        importpath = "github.com/golang/groupcache",
+    )
+
+    go_repository(
+        name = "com_github_golangplus_bytes",
+        commit = "45c989fe5450",
+        importpath = "github.com/golangplus/bytes",
+    )
+
+    go_repository(
+        name = "com_github_golangplus_fmt",
+        commit = "2a5d6d7d2995",
+        importpath = "github.com/golangplus/fmt",
+    )
+
+    go_repository(
+        name = "com_github_golangplus_testing",
+        commit = "af21d9c3145e",
+        importpath = "github.com/golangplus/testing",
+    )
+
+    go_repository(
+        name = "com_github_google_martian",
+        importpath = "github.com/google/martian",
+        tag = "v2.1.0",
+    )
+
+    go_repository(
+        name = "com_github_google_pprof",
+        commit = "3ea8567a2e57",
+        importpath = "github.com/google/pprof",
+    )
+
+    go_repository(
+        name = "com_github_google_uuid",
+        importpath = "github.com/google/uuid",
+        tag = "v1.1.1",
+    )
+
+    go_repository(
+        name = "com_github_googleapis_gax_go_v2",
+        importpath = "github.com/googleapis/gax-go/v2",
+        tag = "v2.0.4",
+    )
+
+    go_repository(
+        name = "com_github_gorilla_websocket",
+        importpath = "github.com/gorilla/websocket",
+        tag = "v1.4.0",
+    )
+
+    go_repository(
+        name = "com_github_grpc_ecosystem_go_grpc_middleware",
+        commit = "f849b5445de4",
+        importpath = "github.com/grpc-ecosystem/go-grpc-middleware",
+    )
+
+    go_repository(
+        name = "com_github_grpc_ecosystem_go_grpc_prometheus",
+        importpath = "github.com/grpc-ecosystem/go-grpc-prometheus",
+        tag = "v1.2.0",
+    )
+
+    go_repository(
+        name = "com_github_hashicorp_golang_lru",
+        importpath = "github.com/hashicorp/golang-lru",
+        tag = "v0.5.1",
+    )
+
+    go_repository(
+        name = "com_github_hashicorp_hcl",
+        importpath = "github.com/hashicorp/hcl",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_jonboulle_clockwork",
+        importpath = "github.com/jonboulle/clockwork",
+        tag = "v0.1.0",
+    )
+
+    go_repository(
+        name = "com_github_jstemmer_go_junit_report",
+        commit = "af01ea7f8024",
+        importpath = "github.com/jstemmer/go-junit-report",
+    )
+
+    go_repository(
+        name = "com_github_julienschmidt_httprouter",
+        importpath = "github.com/julienschmidt/httprouter",
+        tag = "v1.2.0",
+    )
+
+    go_repository(
+        name = "com_github_kisielk_errcheck",
+        importpath = "github.com/kisielk/errcheck",
+        tag = "v1.2.0",
+    )
+
+    go_repository(
+        name = "com_github_konsorten_go_windows_terminal_sequences",
+        importpath = "github.com/konsorten/go-windows-terminal-sequences",
+        tag = "v1.0.1",
+    )
+
+    go_repository(
+        name = "com_github_kr_logfmt",
+        commit = "b84e30acd515",
+        importpath = "github.com/kr/logfmt",
+    )
+
+    go_repository(
+        name = "com_github_liggitt_tabwriter",
+        commit = "89fcab3d43de",
+        importpath = "github.com/liggitt/tabwriter",
+    )
+
+    go_repository(
+        name = "com_github_lithammer_dedent",
+        importpath = "github.com/lithammer/dedent",
+        tag = "v1.1.0",
+    )
+
+    go_repository(
+        name = "com_github_magiconair_properties",
+        importpath = "github.com/magiconair/properties",
+        tag = "v1.8.0",
+    )
+
+    go_repository(
+        name = "com_github_makenowjust_heredoc",
+        commit = "bb23615498cd",
+        importpath = "github.com/MakeNowJust/heredoc",
+    )
+
+    go_repository(
+        name = "com_github_mattn_go_colorable",
+        importpath = "github.com/mattn/go-colorable",
+        tag = "v0.0.9",
+    )
+
+    go_repository(
+        name = "com_github_mattn_go_runewidth",
+        importpath = "github.com/mattn/go-runewidth",
+        tag = "v0.0.2",
+    )
+
+    go_repository(
+        name = "com_github_mitchellh_go_homedir",
+        importpath = "github.com/mitchellh/go-homedir",
+        tag = "v1.1.0",
+    )
+
+    go_repository(
+        name = "com_github_morikuni_aec",
+        importpath = "github.com/morikuni/aec",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_munnerz_goautoneg",
+        commit = "a7dc8b61c822",
+        importpath = "github.com/munnerz/goautoneg",
+    )
+
+    go_repository(
+        name = "com_github_mwitkow_go_conntrack",
+        commit = "cc309e4a2223",
+        importpath = "github.com/mwitkow/go-conntrack",
+    )
+
+    go_repository(
+        name = "com_github_mxk_go_flowrate",
+        commit = "cca7078d478f",
+        importpath = "github.com/mxk/go-flowrate",
+    )
+
+    go_repository(
+        name = "com_github_nytimes_gziphandler",
+        commit = "56545f4a5d46",
+        importpath = "github.com/NYTimes/gziphandler",
+    )
+
+    go_repository(
+        name = "com_github_olekukonko_tablewriter",
+        commit = "a0225b3f23b5",
+        importpath = "github.com/olekukonko/tablewriter",
+    )
+
+    go_repository(
+        name = "com_github_pelletier_go_toml",
+        importpath = "github.com/pelletier/go-toml",
+        tag = "v1.2.0",
+    )
+
+    go_repository(
+        name = "com_github_pquerna_cachecontrol",
+        commit = "0dec1b30a021",
+        importpath = "github.com/pquerna/cachecontrol",
+    )
+
+    go_repository(
+        name = "com_github_remyoudompheng_bigfft",
+        commit = "52369c62f446",
+        importpath = "github.com/remyoudompheng/bigfft",
+    )
+
+    go_repository(
+        name = "com_github_rogpeppe_fastuuid",
+        commit = "6724a57986af",
+        importpath = "github.com/rogpeppe/fastuuid",
+    )
+
+    go_repository(
+        name = "com_github_russross_blackfriday",
+        importpath = "github.com/russross/blackfriday",
+        tag = "v1.5.2",
+    )
+
+    go_repository(
+        name = "com_github_soheilhy_cmux",
+        importpath = "github.com/soheilhy/cmux",
+        tag = "v0.1.4",
+    )
+
+    go_repository(
+        name = "com_github_spf13_afero",
+        importpath = "github.com/spf13/afero",
+        tag = "v1.2.2",
+    )
+
+    go_repository(
+        name = "com_github_spf13_cast",
+        importpath = "github.com/spf13/cast",
+        tag = "v1.3.0",
+    )
+
+    go_repository(
+        name = "com_github_spf13_jwalterweatherman",
+        importpath = "github.com/spf13/jwalterweatherman",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "com_github_spf13_viper",
+        importpath = "github.com/spf13/viper",
+        tag = "v1.3.2",
+    )
+
+    go_repository(
+        name = "com_github_tmc_grpc_websocket_proxy",
+        commit = "89b8d40f7ca8",
+        importpath = "github.com/tmc/grpc-websocket-proxy",
+    )
+
+    go_repository(
+        name = "com_github_ugorji_go_codec",
+        commit = "d75b2dcb6bc8",
+        importpath = "github.com/ugorji/go/codec",
+    )
+
+    go_repository(
+        name = "com_github_xiang90_probing",
+        commit = "43a291ad63a2",
+        importpath = "github.com/xiang90/probing",
+    )
+
+    go_repository(
+        name = "com_github_xlab_handysort",
+        commit = "fb3537ed64a1",
+        importpath = "github.com/xlab/handysort",
+    )
+
+    go_repository(
+        name = "com_github_xordataexchange_crypt",
+        commit = "b2862e3d0a77",
+        importpath = "github.com/xordataexchange/crypt",
+    )
+
+    go_repository(
+        name = "in_gopkg_alecthomas_kingpin_v2",
+        importpath = "gopkg.in/alecthomas/kingpin.v2",
+        tag = "v2.2.6",
+    )
+
+    go_repository(
+        name = "in_gopkg_cheggaaa_pb_v1",
+        importpath = "gopkg.in/cheggaaa/pb.v1",
+        tag = "v1.0.25",
+    )
+
+    go_repository(
+        name = "in_gopkg_natefinch_lumberjack_v2",
+        importpath = "gopkg.in/natefinch/lumberjack.v2",
+        tag = "v2.0.0",
+    )
+
+    go_repository(
+        name = "in_gopkg_resty_v1",
+        importpath = "gopkg.in/resty.v1",
+        tag = "v1.12.0",
+    )
+
+    go_repository(
+        name = "in_gopkg_square_go_jose_v2",
+        importpath = "gopkg.in/square/go-jose.v2",
+        tag = "v2.2.2",
+    )
+
+    go_repository(
+        name = "io_etcd_go_bbolt",
+        importpath = "go.etcd.io/bbolt",
+        tag = "v1.3.3",
+    )
+
+    go_repository(
+        name = "io_etcd_go_etcd",
+        commit = "3cf2f69b5738",
+        importpath = "go.etcd.io/etcd",
+    )
+
+    go_repository(
+        name = "io_k8s_apiserver",
+        commit = "f2537b84c964",
+        importpath = "k8s.io/apiserver",
+    )
+
+    go_repository(
+        name = "io_k8s_cli_runtime",
+        commit = "ec04ad4dbd24",
+        importpath = "k8s.io/cli-runtime",
+    )
+
+    go_repository(
+        name = "io_k8s_code_generator",
+        commit = "2a85f169f05f",
+        importpath = "k8s.io/code-generator",
+    )
+
+    go_repository(
+        name = "io_k8s_component_base",
+        commit = "ea09a2678486",
+        importpath = "k8s.io/component-base",
+    )
+
+    go_repository(
+        name = "io_k8s_gengo",
+        commit = "26a664648505",
+        importpath = "k8s.io/gengo",
+    )
+
+    go_repository(
+        name = "io_k8s_kubectl",
+        commit = "fbc5d36fee2d",
+        importpath = "k8s.io/kubectl",
+    )
+
+    go_repository(
+        name = "io_k8s_metrics",
+        commit = "dea8d0e6b550",
+        importpath = "k8s.io/metrics",
+    )
+
+    go_repository(
+        name = "io_k8s_sigs_kustomize",
+        importpath = "sigs.k8s.io/kustomize",
+        tag = "v2.0.3",
+    )
+
+    go_repository(
+        name = "io_k8s_sigs_structured_merge_diff",
+        commit = "b1b620dd3f06",
+        importpath = "sigs.k8s.io/structured-merge-diff",
+    )
+
+    go_repository(
+        name = "org_golang_x_exp",
+        commit = "4b39c73a6495",
+        importpath = "golang.org/x/exp",
+    )
+
+    go_repository(
+        name = "org_golang_x_image",
+        commit = "0694c2d4d067",
+        importpath = "golang.org/x/image",
+    )
+
+    go_repository(
+        name = "org_golang_x_mobile",
+        commit = "d3739f865fa6",
+        importpath = "golang.org/x/mobile",
+    )
+
+    go_repository(
+        name = "org_golang_x_xerrors",
+        commit = "a985d3407aa7",
+        importpath = "golang.org/x/xerrors",
+    )
+
+    go_repository(
+        name = "org_gonum_v1_gonum",
+        commit = "3d26580ed485",
+        importpath = "gonum.org/v1/gonum",
+    )
+
+    go_repository(
+        name = "org_gonum_v1_netlib",
+        commit = "76723241ea4e",
+        importpath = "gonum.org/v1/netlib",
+    )
+
+    go_repository(
+        name = "org_modernc_cc",
+        importpath = "modernc.org/cc",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "org_modernc_golex",
+        importpath = "modernc.org/golex",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "org_modernc_mathutil",
+        importpath = "modernc.org/mathutil",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "org_modernc_strutil",
+        importpath = "modernc.org/strutil",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "org_modernc_xc",
+        importpath = "modernc.org/xc",
+        tag = "v1.0.0",
+    )
+
+    go_repository(
+        name = "org_uber_go_atomic",
+        importpath = "go.uber.org/atomic",
+        tag = "v1.3.2",
+    )
+
+    go_repository(
+        name = "org_uber_go_multierr",
+        importpath = "go.uber.org/multierr",
+        tag = "v1.1.0",
+    )
+
+    go_repository(
+        name = "org_uber_go_zap",
+        importpath = "go.uber.org/zap",
+        tag = "v1.10.0",
+    )
+
+    go_repository(
+        name = "com_github_dgraph_io_ristretto",
+        commit = "83508260cb49a2c3261c2774c991870fd18b5a1b",
+        importpath = "github.com/dgraph-io/ristretto",
+    )
+
+    go_repository(
+        name = "com_github_cespare_xxhash",
+        commit = "d7df74196a9e781ede915320c11c378c1b2f3a1f",
+        importpath = "github.com/cespare/xxhash",
+    )
+
+    go_repository(
+        name = "com_github_ulule_limiter_v3",
+        commit = "6911899e37a5788df86f770b3f85c1c3eb0313d5",
+        importpath = "github.com/ulule/limiter/v3",
+        remote = "https://github.com/ulule/limiter",
+        vcs = "git",
+    )
+
+    go_repository(
+        name = "com_github_go_telegram_bot_api_telegram_bot_api",
+        commit = "b33efeebc78563cfeddf19563781cffb16aaabdf",
+        importpath = "github.com/go-telegram-bot-api/telegram-bot-api",
+    )
+
+    go_repository(
+        name = "com_github_technoweenie_multipartstreamer",
+        commit = "a90a01d73ae432e2611d178c18367fbaa13e0154",
+        importpath = "github.com/technoweenie/multipartstreamer",
+    )
+
+    go_repository(
+        name = "in_gopkg_irc_v3",
+        commit = "d07dcb9293789fdc99c797d3499a5799bc343b86",
+        importpath = "gopkg.in/irc.v3",
+    )
+
+    go_repository(
+        name = "in_gopkg_russross_blackfriday_v2",
+        commit = "d3b5b032dc8e8927d31a5071b56e14c89f045135",
+        importpath = "gopkg.in/russross/blackfriday.v2",
+    )
+
+    go_repository(
+        name = "com_github_shurcool_sanitized_anchor_name",
+        commit = "7bfe4c7ecddb3666a94b053b422cdd8f5aaa3615",
+        importpath = "github.com/shurcooL/sanitized_anchor_name",
+    )
+
+    go_repository(
+        name = "com_github_go_git_go_billy_v5",
+        commit = "d7a8afccaed297c30f8dff5724dbe422b491dd0d",
+        importpath = "github.com/go-git/go-billy/v5",
+        remote = "https://github.com/go-git/go-billy",
+        vcs = "git",
+    )
+
+    go_repository(
+        name = "com_github_go_git_go_git_v5",
+        commit = "3127ad9a44a2ee935502816065dfe39f494f583d",
+        importpath = "github.com/go-git/go-git/v5",
+        remote = "https://github.com/go-git/go-git",
+        vcs = "git",
+        build_extra_args = [
+            "-known_import=github.com/go-git/go-billy/v5",
+        ],
+    )
+
+    go_repository(
+        name = "com_github_go_git_gcfg",
+        commit = "22f18f9a74d34e3b1a7d59cfa33043bc50ebe376",
+        importpath = "github.com/go-git/gcfg",
+    )
+
+    go_repository(
+        name = "in_gopkg_warnings_v0",
+        commit = "ec4a0fea49c7b46c2aeb0b51aac55779c607e52b",
+        importpath = "gopkg.in/warnings.v0",
+    )
+
+    go_repository(
+        name = "com_github_emirpasic_gods",
+        commit = "80e934ed68b9084f386ae25f74f839aaecfb54d8",
+        importpath = "github.com/emirpasic/gods",
+    )
+
+    go_repository(
+        name = "com_github_jbenet_go_context",
+        commit = "d14ea06fba99483203c19d92cfcd13ebe73135f4",
+        importpath = "github.com/jbenet/go-context",
+    )
+
+    go_repository(
+        name = "com_github_kevinburke_ssh_config",
+        commit = "01f96b0aa0cdcaa93f9495f89bbc6cb5a992ce6e",
+        importpath = "github.com/kevinburke/ssh_config",
+    )
+
+    go_repository(
+        name = "com_github_xanzy_ssh_agent",
+        commit = "6a3e2ff9e7c564f36873c2e36413f634534f1c44",
+        importpath = "github.com/xanzy/ssh-agent",
+    )
+
+    go_repository(
+        name = "com_github_gabriel_vasile_mimetype",
+        commit = "06500030e7d26826f68caa5ca7d98c315c4caa28",
+        importpath = "github.com/gabriel-vasile/mimetype",
+    )
+
+    go_repository(
+        name = "com_github_kevinburke_go_bindata",
+        commit = "a606d617e1d1546a2342de6fc4ed95c78e171d68",
+        importpath = "github.com/kevinburke/go-bindata",
+    )
diff --git a/third_party/java/BUILD b/third_party/java/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/java/BUILD
diff --git a/third_party/java/maven_install.json b/third_party/java/maven_install.json
new file mode 100644
index 0000000..cc4555c
--- /dev/null
+++ b/third_party/java/maven_install.json
@@ -0,0 +1,1211 @@
+{
+    "dependency_tree": {
+        "conflict_resolution": {},
+        "dependencies": [
+            {
+                "coord": "com.fasterxml.jackson.core:jackson-core:2.10.1",
+                "file": "v1/https/repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.jar",
+                    "https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.10.1/jackson-core-2.10.1.jar"
+                ],
+                "sha256": "79bffbdcd349f69a5ac252e2b4096131704386af4fa14d95395ea9a0e423cf33"
+            },
+            {
+                "coord": "com.google.android:annotations:4.1.1.4",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/android/annotations/4.1.1.4/annotations-4.1.1.4.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/android/annotations/4.1.1.4/annotations-4.1.1.4.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/android/annotations/4.1.1.4/annotations-4.1.1.4.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/android/annotations/4.1.1.4/annotations-4.1.1.4.jar",
+                    "https://repo1.maven.org/maven2/com/google/android/annotations/4.1.1.4/annotations-4.1.1.4.jar"
+                ],
+                "sha256": "ba734e1e84c09d615af6a09d33034b4f0442f8772dec120efb376d86a565ae15"
+            },
+            {
+                "coord": "com.google.api.grpc:proto-google-common-protos:1.17.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/api/grpc/proto-google-common-protos/1.17.0/proto-google-common-protos-1.17.0.jar",
+                "directDependencies": [
+                    "com.google.protobuf:protobuf-java:3.11.0"
+                ],
+                "dependencies": [
+                    "com.google.protobuf:protobuf-java:3.11.0"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/api/grpc/proto-google-common-protos/1.17.0/proto-google-common-protos-1.17.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/api/grpc/proto-google-common-protos/1.17.0/proto-google-common-protos-1.17.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/api/grpc/proto-google-common-protos/1.17.0/proto-google-common-protos-1.17.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/api/grpc/proto-google-common-protos/1.17.0/proto-google-common-protos-1.17.0.jar"
+                ],
+                "sha256": "ad25472c73ee470606fb500b376ae5a97973d5406c2f5c3b7d07fb25b4648b65"
+            },
+            {
+                "coord": "com.google.auth:google-auth-library-credentials:0.20.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/auth/google-auth-library-credentials/0.20.0/google-auth-library-credentials-0.20.0.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/auth/google-auth-library-credentials/0.20.0/google-auth-library-credentials-0.20.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/auth/google-auth-library-credentials/0.20.0/google-auth-library-credentials-0.20.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/auth/google-auth-library-credentials/0.20.0/google-auth-library-credentials-0.20.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/auth/google-auth-library-credentials/0.20.0/google-auth-library-credentials-0.20.0.jar"
+                ],
+                "sha256": "8a415273a5dae5c8f9080134e53b9592dc171ca5d13127488c910177c5903bd6"
+            },
+            {
+                "coord": "com.google.auth:google-auth-library-oauth2-http:0.20.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/auth/google-auth-library-oauth2-http/0.20.0/google-auth-library-oauth2-http-0.20.0.jar",
+                "directDependencies": [
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.auto.value:auto-value-annotations:1.7",
+                    "com.google.auth:google-auth-library-credentials:0.20.0",
+                    "com.google.http-client:google-http-client-jackson2:1.34.0",
+                    "com.google.http-client:google-http-client:1.34.0",
+                    "com.google.guava:guava:28.2-android"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.apache.httpcomponents:httpclient:4.5.10",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "commons-logging:commons-logging:1.2",
+                    "io.opencensus:opencensus-contrib-http-util:0.24.0",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.auto.value:auto-value-annotations:1.7",
+                    "com.google.auth:google-auth-library-credentials:0.20.0",
+                    "io.grpc:grpc-context:1.29.0",
+                    "commons-codec:commons-codec:1.11",
+                    "io.opencensus:opencensus-api:0.24.0",
+                    "com.google.http-client:google-http-client-jackson2:1.34.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.fasterxml.jackson.core:jackson-core:2.10.1",
+                    "com.google.http-client:google-http-client:1.34.0",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "org.apache.httpcomponents:httpcore:4.4.12",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/auth/google-auth-library-oauth2-http/0.20.0/google-auth-library-oauth2-http-0.20.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/auth/google-auth-library-oauth2-http/0.20.0/google-auth-library-oauth2-http-0.20.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/auth/google-auth-library-oauth2-http/0.20.0/google-auth-library-oauth2-http-0.20.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/auth/google-auth-library-oauth2-http/0.20.0/google-auth-library-oauth2-http-0.20.0.jar"
+                ],
+                "sha256": "43e96e8c07285c2887042eda4e35ca96522ef361f6c1843f469039d9ccdc8f8a"
+            },
+            {
+                "coord": "com.google.auto.value:auto-value-annotations:1.7",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/auto/value/auto-value-annotations/1.7/auto-value-annotations-1.7.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/auto/value/auto-value-annotations/1.7/auto-value-annotations-1.7.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/auto/value/auto-value-annotations/1.7/auto-value-annotations-1.7.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/auto/value/auto-value-annotations/1.7/auto-value-annotations-1.7.jar",
+                    "https://repo1.maven.org/maven2/com/google/auto/value/auto-value-annotations/1.7/auto-value-annotations-1.7.jar"
+                ],
+                "sha256": "b134bab5082e9f49f2b45802573c78e0726e059b645323645da03e328e501f86"
+            },
+            {
+                "coord": "com.google.code.findbugs:jsr305:3.0.2",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar",
+                    "https://repo1.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar"
+                ],
+                "sha256": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7"
+            },
+            {
+                "coord": "com.google.code.gson:gson:2.8.6",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar",
+                    "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar"
+                ],
+                "sha256": "c8fb4839054d280b3033f800d1f5a97de2f028eb8ba2eb458ad287e536f3f25f"
+            },
+            {
+                "coord": "com.google.errorprone:error_prone_annotations:2.3.4",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar",
+                    "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar"
+                ],
+                "sha256": "baf7d6ea97ce606c53e11b6854ba5f2ce7ef5c24dddf0afa18d1260bd25b002c"
+            },
+            {
+                "coord": "com.google.guava:failureaccess:1.0.1",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar",
+                    "https://repo1.maven.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar"
+                ],
+                "sha256": "a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26"
+            },
+            {
+                "coord": "com.google.guava:guava:28.2-android",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/guava/guava/28.2-android/guava-28.2-android.jar",
+                "directDependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/guava/guava/28.2-android/guava-28.2-android.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/guava/guava/28.2-android/guava-28.2-android.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/guava/guava/28.2-android/guava-28.2-android.jar",
+                    "https://repo1.maven.org/maven2/com/google/guava/guava/28.2-android/guava-28.2-android.jar"
+                ],
+                "sha256": "1faf214c94723ab9fbadfedd9af88ddc325faf669e68eab04688c3afcf59c037"
+            },
+            {
+                "coord": "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar",
+                    "https://repo1.maven.org/maven2/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar"
+                ],
+                "sha256": "b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99"
+            },
+            {
+                "coord": "com.google.http-client:google-http-client-jackson2:1.34.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/http-client/google-http-client-jackson2/1.34.0/google-http-client-jackson2-1.34.0.jar",
+                "directDependencies": [
+                    "com.fasterxml.jackson.core:jackson-core:2.10.1",
+                    "com.google.http-client:google-http-client:1.34.0"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.apache.httpcomponents:httpclient:4.5.10",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "commons-logging:commons-logging:1.2",
+                    "io.opencensus:opencensus-contrib-http-util:0.24.0",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "commons-codec:commons-codec:1.11",
+                    "io.opencensus:opencensus-api:0.24.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.fasterxml.jackson.core:jackson-core:2.10.1",
+                    "com.google.http-client:google-http-client:1.34.0",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "org.apache.httpcomponents:httpcore:4.4.12",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/http-client/google-http-client-jackson2/1.34.0/google-http-client-jackson2-1.34.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/http-client/google-http-client-jackson2/1.34.0/google-http-client-jackson2-1.34.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/http-client/google-http-client-jackson2/1.34.0/google-http-client-jackson2-1.34.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/http-client/google-http-client-jackson2/1.34.0/google-http-client-jackson2-1.34.0.jar"
+                ],
+                "sha256": "c6c2d55048c880f0a26d3e01eb4f1c686284501397793ff6fc8239e0fd368dcc"
+            },
+            {
+                "coord": "com.google.http-client:google-http-client:1.34.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/http-client/google-http-client/1.34.0/google-http-client-1.34.0.jar",
+                "directDependencies": [
+                    "org.apache.httpcomponents:httpclient:4.5.10",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "io.opencensus:opencensus-contrib-http-util:0.24.0",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.opencensus:opencensus-api:0.24.0",
+                    "org.apache.httpcomponents:httpcore:4.4.12",
+                    "com.google.guava:guava:28.2-android"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.apache.httpcomponents:httpclient:4.5.10",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "commons-logging:commons-logging:1.2",
+                    "io.opencensus:opencensus-contrib-http-util:0.24.0",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "commons-codec:commons-codec:1.11",
+                    "io.opencensus:opencensus-api:0.24.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "org.apache.httpcomponents:httpcore:4.4.12",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/http-client/google-http-client/1.34.0/google-http-client-1.34.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/http-client/google-http-client/1.34.0/google-http-client-1.34.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/http-client/google-http-client/1.34.0/google-http-client-1.34.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/http-client/google-http-client/1.34.0/google-http-client-1.34.0.jar"
+                ],
+                "sha256": "376abdc782970145c673446c119bbb158641bca1b311d6098adc238c58be5ed7"
+            },
+            {
+                "coord": "com.google.j2objc:j2objc-annotations:1.3",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar",
+                    "https://repo1.maven.org/maven2/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar"
+                ],
+                "sha256": "21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b"
+            },
+            {
+                "coord": "com.google.protobuf:protobuf-java-util:3.11.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java-util/3.11.0/protobuf-java-util-3.11.0.jar",
+                "directDependencies": [
+                    "com.google.code.gson:gson:2.8.6",
+                    "com.google.protobuf:protobuf-java:3.11.0"
+                ],
+                "dependencies": [
+                    "com.google.protobuf:protobuf-java:3.11.0",
+                    "com.google.code.gson:gson:2.8.6"
+                ],
+                "exclusions": [
+                    "com.google.guava:guava",
+                    "com.google.errorprone:error_prone_annotations"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java-util/3.11.0/protobuf-java-util-3.11.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/protobuf/protobuf-java-util/3.11.0/protobuf-java-util-3.11.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/protobuf/protobuf-java-util/3.11.0/protobuf-java-util-3.11.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java-util/3.11.0/protobuf-java-util-3.11.0.jar"
+                ],
+                "sha256": "54b4cb99ec9196f24d04a47f98416274e34aee16ebb860d5655c6c5a069705e5"
+            },
+            {
+                "coord": "com.google.protobuf:protobuf-java:3.11.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar"
+                ],
+                "sha256": "123d8b260cfdbb91a208931c2d3ea0988c6942f4f4d9303b008f005ef3a4124c"
+            },
+            {
+                "coord": "com.google.protobuf:protobuf-java:3.11.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "exclusions": [
+                    "com.google.guava:guava",
+                    "com.google.errorprone:error_prone_annotations"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar",
+                    "https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.11.0/protobuf-java-3.11.0.jar"
+                ],
+                "sha256": "123d8b260cfdbb91a208931c2d3ea0988c6942f4f4d9303b008f005ef3a4124c"
+            },
+            {
+                "coord": "com.google.truth:truth:1.0.1",
+                "file": "v1/https/repo1.maven.org/maven2/com/google/truth/truth/1.0.1/truth-1.0.1.jar",
+                "directDependencies": [
+                    "com.google.auto.value:auto-value-annotations:1.7",
+                    "junit:junit:4.12",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.googlecode.java-diff-utils:diffutils:1.3.0",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.auto.value:auto-value-annotations:1.7",
+                    "junit:junit:4.12",
+                    "org.hamcrest:hamcrest-core:1.3",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.googlecode.java-diff-utils:diffutils:1.3.0",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/google/truth/truth/1.0.1/truth-1.0.1.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/google/truth/truth/1.0.1/truth-1.0.1.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/google/truth/truth/1.0.1/truth-1.0.1.jar",
+                    "https://repo1.maven.org/maven2/com/google/truth/truth/1.0.1/truth-1.0.1.jar"
+                ],
+                "sha256": "1ccf4334e7a94cf00a20a619b5462b53acf3274e00b70498bf5b28a3bc1be9b1"
+            },
+            {
+                "coord": "com.googlecode.java-diff-utils:diffutils:1.3.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar",
+                    "https://repo1.maven.org/maven2/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar"
+                ],
+                "sha256": "61ba4dc49adca95243beaa0569adc2a23aedb5292ae78aa01186fa782ebdc5c2"
+            },
+            {
+                "coord": "com.squareup.okhttp:okhttp:2.7.4",
+                "file": "v1/https/repo1.maven.org/maven2/com/squareup/okhttp/okhttp/2.7.4/okhttp-2.7.4.jar",
+                "directDependencies": [
+                    "com.squareup.okio:okio:1.13.0"
+                ],
+                "dependencies": [
+                    "com.squareup.okio:okio:1.13.0"
+                ],
+                "url": "https://repo1.maven.org/maven2/com/squareup/okhttp/okhttp/2.7.4/okhttp-2.7.4.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/squareup/okhttp/okhttp/2.7.4/okhttp-2.7.4.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/squareup/okhttp/okhttp/2.7.4/okhttp-2.7.4.jar",
+                    "https://repo1.maven.org/maven2/com/squareup/okhttp/okhttp/2.7.4/okhttp-2.7.4.jar"
+                ],
+                "sha256": "c88be9af1509d5aeec9394a818c0fa08e26fad9d64ba134e6f977e0bb20cb114"
+            },
+            {
+                "coord": "com.squareup.okio:okio:1.13.0",
+                "file": "v1/https/repo1.maven.org/maven2/com/squareup/okio/okio/1.13.0/okio-1.13.0.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/com/squareup/okio/okio/1.13.0/okio-1.13.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/com/squareup/okio/okio/1.13.0/okio-1.13.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/com/squareup/okio/okio/1.13.0/okio-1.13.0.jar",
+                    "https://repo1.maven.org/maven2/com/squareup/okio/okio/1.13.0/okio-1.13.0.jar"
+                ],
+                "sha256": "734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850"
+            },
+            {
+                "coord": "commons-codec:commons-codec:1.11",
+                "file": "v1/https/repo1.maven.org/maven2/commons-codec/commons-codec/1.11/commons-codec-1.11.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/commons-codec/commons-codec/1.11/commons-codec-1.11.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/commons-codec/commons-codec/1.11/commons-codec-1.11.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/commons-codec/commons-codec/1.11/commons-codec-1.11.jar",
+                    "https://repo1.maven.org/maven2/commons-codec/commons-codec/1.11/commons-codec-1.11.jar"
+                ],
+                "sha256": "e599d5318e97aa48f42136a2927e6dfa4e8881dff0e6c8e3109ddbbff51d7b7d"
+            },
+            {
+                "coord": "commons-lang:commons-lang:2.6",
+                "file": "v1/https/repo1.maven.org/maven2/commons-lang/commons-lang/2.6/commons-lang-2.6.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/commons-lang/commons-lang/2.6/commons-lang-2.6.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/commons-lang/commons-lang/2.6/commons-lang-2.6.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/commons-lang/commons-lang/2.6/commons-lang-2.6.jar",
+                    "https://repo1.maven.org/maven2/commons-lang/commons-lang/2.6/commons-lang-2.6.jar"
+                ],
+                "sha256": "50f11b09f877c294d56f24463f47d28f929cf5044f648661c0f0cfbae9a2f49c"
+            },
+            {
+                "coord": "commons-logging:commons-logging:1.2",
+                "file": "v1/https/repo1.maven.org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/commons-logging/commons-logging/1.2/commons-logging-1.2.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/commons-logging/commons-logging/1.2/commons-logging-1.2.jar",
+                    "https://repo1.maven.org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.jar"
+                ],
+                "sha256": "daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636"
+            },
+            {
+                "coord": "io.grpc:grpc-api:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-api/1.29.0/grpc-api-1.29.0.jar",
+                "directDependencies": [
+                    "org.codehaus.mojo:animal-sniffer-annotations:1.18",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:guava:28.2-android"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.codehaus.mojo:animal-sniffer-annotations:1.18",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-api/1.29.0/grpc-api-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-api/1.29.0/grpc-api-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-api/1.29.0/grpc-api-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-api/1.29.0/grpc-api-1.29.0.jar"
+                ],
+                "sha256": "4837824acdd8d576d7d31a862e7391c38a1824cd2224daa68999377fdff9ae3f"
+            },
+            {
+                "coord": "io.grpc:grpc-context:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-context/1.29.0/grpc-context-1.29.0.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-context/1.29.0/grpc-context-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-context/1.29.0/grpc-context-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-context/1.29.0/grpc-context-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-context/1.29.0/grpc-context-1.29.0.jar"
+                ],
+                "sha256": "41426f8fa5b5ff6e8cf5d6a7a6e7b1175350bc8c8e11f352e0622e00f99c4a02"
+            },
+            {
+                "coord": "io.grpc:grpc-core:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-core/1.29.0/grpc-core-1.29.0.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-core/1.29.0/grpc-core-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-core/1.29.0/grpc-core-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-core/1.29.0/grpc-core-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-core/1.29.0/grpc-core-1.29.0.jar"
+                ],
+                "sha256": "d45e3ba310cf6a5d8170bcc500507977505614583c341d03c7d91658e49cf028"
+            },
+            {
+                "coord": "io.grpc:grpc-netty-shaded:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-netty-shaded/1.29.0/grpc-netty-shaded-1.29.0.jar",
+                "directDependencies": [
+                    "io.grpc:grpc-core:1.29.0"
+                ],
+                "dependencies": [
+                    "io.grpc:grpc-core:1.29.0"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-netty-shaded/1.29.0/grpc-netty-shaded-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-netty-shaded/1.29.0/grpc-netty-shaded-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-netty-shaded/1.29.0/grpc-netty-shaded-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-netty-shaded/1.29.0/grpc-netty-shaded-1.29.0.jar"
+                ],
+                "sha256": "1e557a2f4b4a1332bb79e7f7354a031491f24e0e45aa493f0d48b555916cf049"
+            },
+            {
+                "coord": "io.grpc:grpc-protobuf-lite:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-protobuf-lite/1.29.0/grpc-protobuf-lite-1.29.0.jar",
+                "directDependencies": [
+                    "com.google.guava:guava:28.2-android",
+                    "io.grpc:grpc-api:1.29.0"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.codehaus.mojo:animal-sniffer-annotations:1.18",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "io.grpc:grpc-api:1.29.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "exclusions": [
+                    "com.google.protobuf:protobuf-javalite"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-protobuf-lite/1.29.0/grpc-protobuf-lite-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-protobuf-lite/1.29.0/grpc-protobuf-lite-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-protobuf-lite/1.29.0/grpc-protobuf-lite-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-protobuf-lite/1.29.0/grpc-protobuf-lite-1.29.0.jar"
+                ],
+                "sha256": "ae4bbcd9bf7ad4856660807d8cba7ef4ff428f0b615bf663ba308d9a76bcab3c"
+            },
+            {
+                "coord": "io.grpc:grpc-protobuf:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-protobuf/1.29.0/grpc-protobuf-1.29.0.jar",
+                "directDependencies": [
+                    "com.google.api.grpc:proto-google-common-protos:1.17.0",
+                    "com.google.protobuf:protobuf-java:3.11.0",
+                    "io.grpc:grpc-api:1.29.0",
+                    "io.grpc:grpc-protobuf-lite:1.29.0",
+                    "com.google.guava:guava:28.2-android"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.codehaus.mojo:animal-sniffer-annotations:1.18",
+                    "com.google.api.grpc:proto-google-common-protos:1.17.0",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.protobuf:protobuf-java:3.11.0",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "io.grpc:grpc-api:1.29.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "io.grpc:grpc-protobuf-lite:1.29.0",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-protobuf/1.29.0/grpc-protobuf-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-protobuf/1.29.0/grpc-protobuf-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-protobuf/1.29.0/grpc-protobuf-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-protobuf/1.29.0/grpc-protobuf-1.29.0.jar"
+                ],
+                "sha256": "ee8cef64c7e10dd373aabd3a4b2ec4878e6d5b3ba43cbf55f3876ddaa79266ea"
+            },
+            {
+                "coord": "io.grpc:grpc-services:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-services/1.29.0/grpc-services-1.29.0.jar",
+                "directDependencies": [
+                    "com.google.protobuf:protobuf-java-util:3.11.0",
+                    "io.grpc:grpc-core:1.29.0",
+                    "io.grpc:grpc-protobuf:1.29.0",
+                    "io.grpc:grpc-stub:1.29.0"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.codehaus.mojo:animal-sniffer-annotations:1.18",
+                    "com.google.api.grpc:proto-google-common-protos:1.17.0",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.protobuf:protobuf-java:3.11.0",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-core:1.29.0",
+                    "com.google.protobuf:protobuf-java-util:3.11.0",
+                    "io.grpc:grpc-context:1.29.0",
+                    "com.google.code.gson:gson:2.8.6",
+                    "io.grpc:grpc-api:1.29.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "io.grpc:grpc-protobuf-lite:1.29.0",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "io.grpc:grpc-stub:1.29.0",
+                    "io.grpc:grpc-protobuf:1.29.0",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-services/1.29.0/grpc-services-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-services/1.29.0/grpc-services-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-services/1.29.0/grpc-services-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-services/1.29.0/grpc-services-1.29.0.jar"
+                ],
+                "sha256": "6bea2f0ec35d3071a12fccc640ca7450f1cd2ce66574456e8deec21f79464681"
+            },
+            {
+                "coord": "io.grpc:grpc-stub:1.29.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/grpc/grpc-stub/1.29.0/grpc-stub-1.29.0.jar",
+                "directDependencies": [
+                    "io.grpc:grpc-api:1.29.0"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "org.codehaus.mojo:animal-sniffer-annotations:1.18",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "io.grpc:grpc-api:1.29.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/grpc/grpc-stub/1.29.0/grpc-stub-1.29.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/grpc/grpc-stub/1.29.0/grpc-stub-1.29.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/grpc/grpc-stub/1.29.0/grpc-stub-1.29.0.jar",
+                    "https://repo1.maven.org/maven2/io/grpc/grpc-stub/1.29.0/grpc-stub-1.29.0.jar"
+                ],
+                "sha256": "65b01e451013d6c9f2de1392abf47190a397cbbd7f5a45e3cc9df509671a0cf8"
+            },
+            {
+                "coord": "io.netty:netty-buffer:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-buffer/4.1.48.Final/netty-buffer-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-common:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-common:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-buffer/4.1.48.Final/netty-buffer-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-buffer/4.1.48.Final/netty-buffer-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-buffer/4.1.48.Final/netty-buffer-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-buffer/4.1.48.Final/netty-buffer-4.1.48.Final.jar"
+                ],
+                "sha256": "7efc8f98224c703ef09a409e5ddffbe14f5b4b6f527d3836c1647b4d9eff8cec"
+            },
+            {
+                "coord": "io.netty:netty-codec-http2:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-codec-http2/4.1.48.Final/netty-codec-http2-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-codec-http:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-handler:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-codec-http:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-handler:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-codec-http2/4.1.48.Final/netty-codec-http2-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-codec-http2/4.1.48.Final/netty-codec-http2-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-codec-http2/4.1.48.Final/netty-codec-http2-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-codec-http2/4.1.48.Final/netty-codec-http2-4.1.48.Final.jar"
+                ],
+                "sha256": "359548f53cf8697ebdfa13a4700f1b9a5585573c64f2d3ed135a3197ebd51579"
+            },
+            {
+                "coord": "io.netty:netty-codec-http:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-codec-http/4.1.48.Final/netty-codec-http-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-handler:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-handler:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-codec-http/4.1.48.Final/netty-codec-http-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-codec-http/4.1.48.Final/netty-codec-http-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-codec-http/4.1.48.Final/netty-codec-http-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-codec-http/4.1.48.Final/netty-codec-http-4.1.48.Final.jar"
+                ],
+                "sha256": "aa4b18070e7fc105f0c94a077605687bec48091274c8acc121116692c335edd0"
+            },
+            {
+                "coord": "io.netty:netty-codec-socks:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-codec-socks/4.1.48.Final/netty-codec-socks-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-transport:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-codec-socks/4.1.48.Final/netty-codec-socks-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-codec-socks/4.1.48.Final/netty-codec-socks-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-codec-socks/4.1.48.Final/netty-codec-socks-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-codec-socks/4.1.48.Final/netty-codec-socks-4.1.48.Final.jar"
+                ],
+                "sha256": "d0dd35f9ac6892a03bb0d38ea32e683993c4308a02de5756bb5a23ecb929f917"
+            },
+            {
+                "coord": "io.netty:netty-codec:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-codec/4.1.48.Final/netty-codec-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-transport:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-codec/4.1.48.Final/netty-codec-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-codec/4.1.48.Final/netty-codec-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-codec/4.1.48.Final/netty-codec-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-codec/4.1.48.Final/netty-codec-4.1.48.Final.jar"
+                ],
+                "sha256": "81b4c316163a591b4f74fd2dc23a3ea45359cb817d0a9c4fc7f37dc9edfdbea8"
+            },
+            {
+                "coord": "io.netty:netty-common:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-common/4.1.48.Final/netty-common-4.1.48.Final.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-common/4.1.48.Final/netty-common-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-common/4.1.48.Final/netty-common-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-common/4.1.48.Final/netty-common-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-common/4.1.48.Final/netty-common-4.1.48.Final.jar"
+                ],
+                "sha256": "e44a2369566fd1fa8a0f30b12e2801de8fb405b9d1fa3894a58b6262065a9916"
+            },
+            {
+                "coord": "io.netty:netty-handler-proxy:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-handler-proxy/4.1.48.Final/netty-handler-proxy-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-codec-http:4.1.48.Final",
+                    "io.netty:netty-codec-socks:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-codec-http:4.1.48.Final",
+                    "io.netty:netty-codec-socks:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-handler:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-handler-proxy/4.1.48.Final/netty-handler-proxy-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-handler-proxy/4.1.48.Final/netty-handler-proxy-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-handler-proxy/4.1.48.Final/netty-handler-proxy-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-handler-proxy/4.1.48.Final/netty-handler-proxy-4.1.48.Final.jar"
+                ],
+                "sha256": "f784f331bdb05834390c132d1534724e5371c1a19c7a62217e5f192963a9a92c"
+            },
+            {
+                "coord": "io.netty:netty-handler:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-handler/4.1.48.Final/netty-handler-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-codec:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-handler/4.1.48.Final/netty-handler-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-handler/4.1.48.Final/netty-handler-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-handler/4.1.48.Final/netty-handler-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-handler/4.1.48.Final/netty-handler-4.1.48.Final.jar"
+                ],
+                "sha256": "757f83c7891ad2ebad209f02d8dbca0121e03f7062c2d4ec9d00eba1a0d403d5"
+            },
+            {
+                "coord": "io.netty:netty-resolver:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-resolver/4.1.48.Final/netty-resolver-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-common:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-common:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-resolver/4.1.48.Final/netty-resolver-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-resolver/4.1.48.Final/netty-resolver-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-resolver/4.1.48.Final/netty-resolver-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-resolver/4.1.48.Final/netty-resolver-4.1.48.Final.jar"
+                ],
+                "sha256": "fb125914398ebef821def3dbb1642f9f360f39d182f00149ef3db845ebf06ad2"
+            },
+            {
+                "coord": "io.netty:netty-tcnative-boringssl-static:2.0.30.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/2.0.30.Final/netty-tcnative-boringssl-static-2.0.30.Final.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/2.0.30.Final/netty-tcnative-boringssl-static-2.0.30.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-tcnative-boringssl-static/2.0.30.Final/netty-tcnative-boringssl-static-2.0.30.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-tcnative-boringssl-static/2.0.30.Final/netty-tcnative-boringssl-static-2.0.30.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/2.0.30.Final/netty-tcnative-boringssl-static-2.0.30.Final.jar"
+                ],
+                "sha256": "61934ca753be47973fe427d1f483a1b2fbcaf56eefc71519bf35fddb036ee111"
+            },
+            {
+                "coord": "io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-transport-native-epoll/4.1.48.Final/netty-transport-native-epoll-4.1.48.Final-linux-x86_64.jar",
+                "directDependencies": [
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-transport-native-unix-common:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-transport-native-unix-common:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-transport-native-epoll/4.1.48.Final/netty-transport-native-epoll-4.1.48.Final-linux-x86_64.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-transport-native-epoll/4.1.48.Final/netty-transport-native-epoll-4.1.48.Final-linux-x86_64.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-transport-native-epoll/4.1.48.Final/netty-transport-native-epoll-4.1.48.Final-linux-x86_64.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-transport-native-epoll/4.1.48.Final/netty-transport-native-epoll-4.1.48.Final-linux-x86_64.jar"
+                ],
+                "sha256": "7436ecfb442b299af6ecff7ae6a8d3f00fb56e081d20e82b467dad2e6ee8848f"
+            },
+            {
+                "coord": "io.netty:netty-transport-native-unix-common:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-transport-native-unix-common/4.1.48.Final/netty-transport-native-unix-common-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-transport:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-transport:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-transport-native-unix-common/4.1.48.Final/netty-transport-native-unix-common-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-transport-native-unix-common/4.1.48.Final/netty-transport-native-unix-common-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-transport-native-unix-common/4.1.48.Final/netty-transport-native-unix-common-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-transport-native-unix-common/4.1.48.Final/netty-transport-native-unix-common-4.1.48.Final.jar"
+                ],
+                "sha256": "c4142429437845d966babba5eddce47203e1f256209e455019d2538ebec58b95"
+            },
+            {
+                "coord": "io.netty:netty-transport:4.1.48.Final",
+                "file": "v1/https/repo1.maven.org/maven2/io/netty/netty-transport/4.1.48.Final/netty-transport-4.1.48.Final.jar",
+                "directDependencies": [
+                    "io.netty:netty-buffer:4.1.48.Final",
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final"
+                ],
+                "dependencies": [
+                    "io.netty:netty-common:4.1.48.Final",
+                    "io.netty:netty-resolver:4.1.48.Final",
+                    "io.netty:netty-buffer:4.1.48.Final"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/netty/netty-transport/4.1.48.Final/netty-transport-4.1.48.Final.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/netty/netty-transport/4.1.48.Final/netty-transport-4.1.48.Final.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/netty/netty-transport/4.1.48.Final/netty-transport-4.1.48.Final.jar",
+                    "https://repo1.maven.org/maven2/io/netty/netty-transport/4.1.48.Final/netty-transport-4.1.48.Final.jar"
+                ],
+                "sha256": "6b4ba9e09a8e060bad2540845491b5fa1ca73614d157860e657f4027c91e72fd"
+            },
+            {
+                "coord": "io.opencensus:opencensus-api:0.24.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar",
+                "directDependencies": [
+                    "io.grpc:grpc-context:1.29.0"
+                ],
+                "dependencies": [
+                    "io.grpc:grpc-context:1.29.0"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar",
+                    "https://repo1.maven.org/maven2/io/opencensus/opencensus-api/0.24.0/opencensus-api-0.24.0.jar"
+                ],
+                "sha256": "f561b1cc2673844288e596ddf5bb6596868a8472fd2cb8993953fc5c034b2352"
+            },
+            {
+                "coord": "io.opencensus:opencensus-contrib-grpc-metrics:0.24.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/opencensus/opencensus-contrib-grpc-metrics/0.24.0/opencensus-contrib-grpc-metrics-0.24.0.jar",
+                "directDependencies": [
+                    "com.google.guava:guava:28.2-android",
+                    "io.opencensus:opencensus-api:0.24.0"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "io.opencensus:opencensus-api:0.24.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/opencensus/opencensus-contrib-grpc-metrics/0.24.0/opencensus-contrib-grpc-metrics-0.24.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/opencensus/opencensus-contrib-grpc-metrics/0.24.0/opencensus-contrib-grpc-metrics-0.24.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/opencensus/opencensus-contrib-grpc-metrics/0.24.0/opencensus-contrib-grpc-metrics-0.24.0.jar",
+                    "https://repo1.maven.org/maven2/io/opencensus/opencensus-contrib-grpc-metrics/0.24.0/opencensus-contrib-grpc-metrics-0.24.0.jar"
+                ],
+                "sha256": "875582e093f11950ad3f4a50b5fee33a008023f7d1e47820a1bef05d23b9ed42"
+            },
+            {
+                "coord": "io.opencensus:opencensus-contrib-http-util:0.24.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar",
+                "directDependencies": [
+                    "com.google.guava:guava:28.2-android",
+                    "io.opencensus:opencensus-api:0.24.0"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "io.grpc:grpc-context:1.29.0",
+                    "io.opencensus:opencensus-api:0.24.0",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar",
+                    "https://repo1.maven.org/maven2/io/opencensus/opencensus-contrib-http-util/0.24.0/opencensus-contrib-http-util-0.24.0.jar"
+                ],
+                "sha256": "7155273bbb1ed3d477ea33cf19d7bbc0b285ff395f43b29ae576722cf247000f"
+            },
+            {
+                "coord": "io.perfmark:perfmark-api:0.19.0",
+                "file": "v1/https/repo1.maven.org/maven2/io/perfmark/perfmark-api/0.19.0/perfmark-api-0.19.0.jar",
+                "directDependencies": [
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.errorprone:error_prone_annotations:2.3.4"
+                ],
+                "dependencies": [
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.errorprone:error_prone_annotations:2.3.4"
+                ],
+                "url": "https://repo1.maven.org/maven2/io/perfmark/perfmark-api/0.19.0/perfmark-api-0.19.0.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/io/perfmark/perfmark-api/0.19.0/perfmark-api-0.19.0.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/io/perfmark/perfmark-api/0.19.0/perfmark-api-0.19.0.jar",
+                    "https://repo1.maven.org/maven2/io/perfmark/perfmark-api/0.19.0/perfmark-api-0.19.0.jar"
+                ],
+                "sha256": "b734ba2149712409a44eabdb799f64768578fee0defe1418bb108fe32ea43e1a"
+            },
+            {
+                "coord": "javax.annotation:javax.annotation-api:1.2",
+                "file": "v1/https/repo1.maven.org/maven2/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar",
+                    "https://repo1.maven.org/maven2/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar"
+                ],
+                "sha256": "5909b396ca3a2be10d0eea32c74ef78d816e1b4ead21de1d78de1f890d033e04"
+            },
+            {
+                "coord": "junit:junit:4.12",
+                "file": "v1/https/repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar",
+                "directDependencies": [
+                    "org.hamcrest:hamcrest-core:1.3"
+                ],
+                "dependencies": [
+                    "org.hamcrest:hamcrest-core:1.3"
+                ],
+                "url": "https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/junit/junit/4.12/junit-4.12.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/junit/junit/4.12/junit-4.12.jar",
+                    "https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar"
+                ],
+                "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a"
+            },
+            {
+                "coord": "net.md-5:bungeecord-chat:jar:1.15-SNAPSHOT",
+                "file": "v1/https/oss.sonatype.org/content/repositories/snapshots/net/md-5/bungeecord-chat/1.15-SNAPSHOT/bungeecord-chat-1.15-20200509.234557-72.jar",
+                "directDependencies": [
+                    "com.google.code.gson:gson:2.8.6",
+                    "com.google.guava:guava:28.2-android"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.code.gson:gson:2.8.6",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://oss.sonatype.org/content/repositories/snapshots/net/md-5/bungeecord-chat/1.15-SNAPSHOT/bungeecord-chat-1.15-20200509.234557-72.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/net/md-5/bungeecord-chat/1.15-SNAPSHOT/bungeecord-chat-1.15-20200509.234557-72.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/net/md-5/bungeecord-chat/1.15-SNAPSHOT/bungeecord-chat-1.15-20200509.234557-72.jar",
+                    "https://repo1.maven.org/maven2/net/md-5/bungeecord-chat/1.15-SNAPSHOT/bungeecord-chat-1.15-20200509.234557-72.jar"
+                ],
+                "sha256": "fd727533e2629f5383feebe28c33ac168c9d210115c832970d9b4338579ac720"
+            },
+            {
+                "coord": "org.apache.commons:commons-lang3:3.5",
+                "file": "v1/https/repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar",
+                    "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar"
+                ],
+                "sha256": "8ac96fc686512d777fca85e144f196cd7cfe0c0aec23127229497d1a38ff651c"
+            },
+            {
+                "coord": "org.apache.httpcomponents:httpclient:4.5.10",
+                "file": "v1/https/repo1.maven.org/maven2/org/apache/httpcomponents/httpclient/4.5.10/httpclient-4.5.10.jar",
+                "directDependencies": [
+                    "commons-codec:commons-codec:1.11",
+                    "commons-logging:commons-logging:1.2",
+                    "org.apache.httpcomponents:httpcore:4.4.12"
+                ],
+                "dependencies": [
+                    "commons-logging:commons-logging:1.2",
+                    "commons-codec:commons-codec:1.11",
+                    "org.apache.httpcomponents:httpcore:4.4.12"
+                ],
+                "url": "https://repo1.maven.org/maven2/org/apache/httpcomponents/httpclient/4.5.10/httpclient-4.5.10.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/apache/httpcomponents/httpclient/4.5.10/httpclient-4.5.10.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/apache/httpcomponents/httpclient/4.5.10/httpclient-4.5.10.jar",
+                    "https://repo1.maven.org/maven2/org/apache/httpcomponents/httpclient/4.5.10/httpclient-4.5.10.jar"
+                ],
+                "sha256": "38b9f16f504928e4db736a433b9cd10968d9ec8d6f5d0e61a64889a689172134"
+            },
+            {
+                "coord": "org.apache.httpcomponents:httpcore:4.4.12",
+                "file": "v1/https/repo1.maven.org/maven2/org/apache/httpcomponents/httpcore/4.4.12/httpcore-4.4.12.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/org/apache/httpcomponents/httpcore/4.4.12/httpcore-4.4.12.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/apache/httpcomponents/httpcore/4.4.12/httpcore-4.4.12.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/apache/httpcomponents/httpcore/4.4.12/httpcore-4.4.12.jar",
+                    "https://repo1.maven.org/maven2/org/apache/httpcomponents/httpcore/4.4.12/httpcore-4.4.12.jar"
+                ],
+                "sha256": "ab765334beabf0ea024484a5e90a7c40e8160b145f22d199e11e27f68d57da08"
+            },
+            {
+                "coord": "org.checkerframework:checker-compat-qual:2.5.5",
+                "file": "v1/https/repo1.maven.org/maven2/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar",
+                    "https://repo1.maven.org/maven2/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar"
+                ],
+                "sha256": "11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a"
+            },
+            {
+                "coord": "org.codehaus.mojo:animal-sniffer-annotations:1.18",
+                "file": "v1/https/repo1.maven.org/maven2/org/codehaus/mojo/animal-sniffer-annotations/1.18/animal-sniffer-annotations-1.18.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/org/codehaus/mojo/animal-sniffer-annotations/1.18/animal-sniffer-annotations-1.18.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/codehaus/mojo/animal-sniffer-annotations/1.18/animal-sniffer-annotations-1.18.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/codehaus/mojo/animal-sniffer-annotations/1.18/animal-sniffer-annotations-1.18.jar",
+                    "https://repo1.maven.org/maven2/org/codehaus/mojo/animal-sniffer-annotations/1.18/animal-sniffer-annotations-1.18.jar"
+                ],
+                "sha256": "47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d"
+            },
+            {
+                "coord": "org.hamcrest:hamcrest-core:1.3",
+                "file": "v1/https/repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar",
+                    "https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar"
+                ],
+                "sha256": "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9"
+            },
+            {
+                "coord": "org.spigotmc:spigot-api:1.15.2-R0.1-SNAPSHOT",
+                "file": "v1/https/hub.spigotmc.org/nexus/content/repositories/snapshots/org/spigotmc/spigot-api/1.15.2-R0.1-SNAPSHOT/spigot-api-1.15.2-R0.1-20200509.094510-108.jar",
+                "directDependencies": [
+                    "commons-lang:commons-lang:2.6",
+                    "org.yaml:snakeyaml:1.25",
+                    "com.google.code.gson:gson:2.8.6",
+                    "net.md-5:bungeecord-chat:jar:1.15-SNAPSHOT",
+                    "com.google.guava:guava:28.2-android"
+                ],
+                "dependencies": [
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "commons-lang:commons-lang:2.6",
+                    "org.yaml:snakeyaml:1.25",
+                    "com.google.code.gson:gson:2.8.6",
+                    "com.google.errorprone:error_prone_annotations:2.3.4",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "net.md-5:bungeecord-chat:jar:1.15-SNAPSHOT",
+                    "com.google.guava:guava:28.2-android",
+                    "org.checkerframework:checker-compat-qual:2.5.5"
+                ],
+                "url": "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/spigotmc/spigot-api/1.15.2-R0.1-SNAPSHOT/spigot-api-1.15.2-R0.1-20200509.094510-108.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/spigotmc/spigot-api/1.15.2-R0.1-SNAPSHOT/spigot-api-1.15.2-R0.1-20200509.094510-108.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/spigotmc/spigot-api/1.15.2-R0.1-SNAPSHOT/spigot-api-1.15.2-R0.1-20200509.094510-108.jar",
+                    "https://repo1.maven.org/maven2/org/spigotmc/spigot-api/1.15.2-R0.1-SNAPSHOT/spigot-api-1.15.2-R0.1-20200509.094510-108.jar"
+                ],
+                "sha256": "243c81927517f29ff2bf51303beba6470cb92ae15f82fadd0d8ba86c5715e5f8"
+            },
+            {
+                "coord": "org.yaml:snakeyaml:1.25",
+                "file": "v1/https/repo1.maven.org/maven2/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar",
+                "directDependencies": [],
+                "dependencies": [],
+                "url": "https://repo1.maven.org/maven2/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar",
+                "mirror_urls": [
+                    "https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar",
+                    "https://oss.sonatype.org/content/repositories/snapshots/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar",
+                    "https://repo1.maven.org/maven2/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar"
+                ],
+                "sha256": "b50ef33187e7dc922b26dbe4dd0fdb3a9cf349e75a08b95269901548eee546eb"
+            }
+        ],
+        "version": "0.1.0",
+        "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 392476608
+    }
+}
diff --git a/third_party/jq/BUILD b/third_party/jq/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/jq/BUILD
diff --git a/third_party/jq/BUILD.external b/third_party/jq/BUILD.external
new file mode 100644
index 0000000..e26c0fb
--- /dev/null
+++ b/third_party/jq/BUILD.external
@@ -0,0 +1,78 @@
+# Copyright 2019 Google LLC
+# Copyright 2020 Sergiusz 'q3k' Bazanski
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The jq binary.
+cc_binary(
+    name = "jq",
+    srcs = [
+        ":source_files",
+    ],
+    copts = [
+        "-Iexternal/com_github_kkos_oniguruma/src",
+        "-Wno-cpp",
+        "-Wno-unused-function",
+        "-Wno-unused-variable",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        "@com_github_kkos_oniguruma//:oniguruma",
+    ],
+)
+
+# Generate the source files.
+# This will run ./configure and extract a host-dependent confdefs.h file, then
+# prefix each .h acd .c file with '#include "confdefs.h"'.
+genrule(
+    name = "source_files",
+    srcs = glob(["**"]),
+    outs = ["build/" + f for f in glob(
+        [
+            "src/*.h",
+            "src/*.c",
+        ],
+        exclude = [
+            "src/inject_errors.c",
+        ],
+    )] + [
+        # The extracted confdefs.h.
+        "build/src/confdefs.h",
+        # The generated builtin.inc.
+        "build/src/builtin.inc",
+    ],
+    cmd =
+        # Run ./configure && make src/builtin.inc.
+        "( " +
+        "  cd external/com_github_stedolan_jq; " +
+        "  ./configure > /dev/null 2> /dev/null; " +
+        "  make src/builtin.inc > /dev/null; " +
+        "); " +
+        # Extract confdefs.h from config.log.
+        "grep '^/\\* confdefs.h \\*/$$' external/com_github_stedolan_jq/config.log -A1000 " +
+        "  | head -n -1 > \"$(@D)\"/build/src/confdefs.h; " +
+        # Prefix each output file with an include of confdefs.h.
+        "OUTS=\"$(OUTS)\"; for FILE in $$OUTS; do " +
+        "  touch \"$$FILE\"; " +
+        "  BASENAME=\"$$(basename $$FILE)\"; " +
+        "  if [ \"$$BASENAME\" != \"confdefs.h\" ]; then " +
+        "    echo '#include \"confdefs.h\"' > \"$$FILE\"; " +
+        "  cat external/com_github_stedolan_jq/src/\"$$BASENAME\" >> \"$$FILE\"; " +
+        "  fi; " +
+        # Replace non-relative references.
+        "  sed -e 's|^#include \"src/|#include \"|g' -i \"$$FILE\"; " +
+        "done; " +
+        # Copy builtin.inc and version.h without modificaitons.
+        "cp external/com_github_stedolan_jq/src/builtin.inc \"$(@D)\"/build/src/builtin.inc; ",
+)
+
diff --git a/third_party/jq/README.md b/third_party/jq/README.md
new file mode 100644
index 0000000..223de52
--- /dev/null
+++ b/third_party/jq/README.md
@@ -0,0 +1,6 @@
+third\_party: jq
+================
+
+Upstream: https://github.com/stedolan/jq
+
+Pulled in by WORKSPACE (com\_github\_stedolan\_jq), BUILD.external used as root BUILDfile for external repository.
diff --git a/third_party/licenses/Apache-2.0.txt b/third_party/licenses/Apache-2.0.txt
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/third_party/licenses/Apache-2.0.txt
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/nix/BUILD b/third_party/nix/BUILD
new file mode 100644
index 0000000..12ae0fa
--- /dev/null
+++ b/third_party/nix/BUILD
@@ -0,0 +1,29 @@
+load("@rules_python//python:defs.bzl", "py_runtime_pair")
+
+# Python toolchain definition that uses //third_party/nix:python.nix (via
+# external repository).
+
+py_runtime(
+    name = "py3_runtime",
+    interpreter = "@hscloud_nix_python3//:python3",
+    python_version = "PY3",
+)
+
+py_runtime(
+    name = "py2_runtime",
+    interpreter = "@hscloud_nix_python2//:python2",
+    python_version = "PY2",
+)
+
+
+py_runtime_pair(
+    name = "py_runtime_pair",
+    py2_runtime = ":py2_runtime",
+    py3_runtime = ":py3_runtime",
+)
+
+toolchain(
+    name = "py_toolchain",
+    toolchain = ":py_runtime_pair",
+    toolchain_type = "@rules_python//python:toolchain_type",
+)
diff --git a/third_party/nix/python.nix b/third_party/nix/python.nix
new file mode 100644
index 0000000..078b711
--- /dev/null
+++ b/third_party/nix/python.nix
@@ -0,0 +1,49 @@
+# This is a Python interpreter wrapper that's passed to pip3_import under
+# NixOS.
+# It allows us to build some pip wheels under NixOS that require special
+# system libraries. This is quite hacky, it would be much better if we could
+# somehow tell pip3_import that a given package needs to be built within a
+# given environment.
+
+with import <nixpkgs> {};
+
+let
+  # We use mkDerivation instead of writeScript or writeScriptBin as we need a
+  # derivation that both:
+  # - has a directory structure (for rules_nixpkgs to be able to use it)
+  # - has the Python interpreter directly in that structure and not in bin/, as
+  #   rules_python's pip3_import interpreter_path requires a file target, and
+  #   will not take an alias. Meanwhile, rules_nixpkgs only creates a BUILD file
+  #   in the root path of the external repository (which is populated with a
+  #   symlink tree from the nix derivation), so we can onlly directly reference
+  #   file in the root of a Nix derivation.
+  generic = package: binary:  stdenv.mkDerivation {
+    name = "${binary}-wrapper";
+    version = "1.0";
+    src = ./.;
+    unpackPhase = "";
+    buildPhase = ''
+      mkdir -p $out
+      cat > $out/${binary} <<EOF
+#!/bin/bash
+
+# pyscopg wants libpq, and uses pg_config to find paths. Inject pg_config into
+# the Python interpreter's path.
+export PATH="${pkgs.postgresql}/bin:\$PATH"
+
+# uWSGI has a truly cheese-grade build system, and this is the only way to let
+# it know where to find ncurses.
+export LDFLAGS="-L${pkgs.ncurses}/lib"
+exec ${package}/bin/${binary} "\$@"
+EOF
+    '';
+    installPhase = ''
+      chmod +x $out/${binary}
+    '';
+  };
+
+in {
+  # Add cffi for import _cffi_backend in `cryptography` to work.
+  python2 = generic (pkgs.python27.withPackages (ps: with ps; [ cffi ])) "python2";
+  python3 = generic (pkgs.python37.withPackages (ps: with ps; [ cffi ])) "python3";
+}
diff --git a/third_party/nix/repository_rules.bzl b/third_party/nix/repository_rules.bzl
new file mode 100644
index 0000000..35c3d2d
--- /dev/null
+++ b/third_party/nix/repository_rules.bzl
@@ -0,0 +1,108 @@
+load("@io_tweag_rules_nixpkgs//nixpkgs:repositories.bzl", "rules_nixpkgs_dependencies")
+load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", "nixpkgs_git_repository", "nixpkgs_package")
+
+def has_nix(ctx):
+    return ctx.which("nix-build") != None
+
+def _hscloud_gen_go_imports_impl(ctx):
+    ctx.file("BUILD", "")
+
+    imports_for_nix = """
+load("@io_tweag_rules_nixpkgs//nixpkgs:toolchains/go.bzl", "nixpkgs_go_configure")
+
+def hscloud_go_register_toolchains():
+    nixpkgs_go_configure(repository = "@nixpkgs")
+"""
+    imports_for_non_nix = """
+load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
+def hscloud_go_register_toolchains():
+    go_register_toolchains()
+"""
+
+    if has_nix(ctx):
+        ctx.file("imports.bzl", imports_for_nix)
+    else:
+        ctx.file("imports.bzl", imports_for_non_nix)
+
+# Generate repository containing either a call to go_register_toolchains() or
+# nixpkgs_go_configure(), depending on nix presence.
+hscloud_gen_go_imports = repository_rule(
+    implementation = _hscloud_gen_go_imports_impl,
+    attrs = dict(),
+)
+
+def _hscloud_gen_pip_imports_impl(ctx):
+    ctx.file("BUILD", "")
+
+    # For Nix, we have to both pass our interpreter to pip3_import, and also
+    # register it as a toolchain.
+    imports_for_nix = """
+load("@rules_python//python:pip.bzl", "pip3_import")
+def hscloud_pip3_import(name, requirements):
+    pip3_import(
+        name = name,
+        requirements = requirements,
+        python_interpreter_target = "@hscloud_nix_python3//:python3",
+    )
+    native.register_toolchains("//third_party/nix:py_toolchain")
+"""
+    imports_for_non_nix = """
+load("@rules_python//python:pip.bzl", "pip3_import")
+def hscloud_pip3_import(name, requirements):
+    pip3_import(
+        name = name,
+        requirements = requirements,
+    )
+"""
+    if has_nix(ctx):
+        ctx.file("imports.bzl", imports_for_nix)
+    else:
+        ctx.file("imports.bzl", imports_for_non_nix)
+
+# Generate repository containing a wrapped pip3_import that either uses the
+# host Python interpreter or one from nixpkgs, depending on nix presence.
+hscloud_gen_pip_imports = repository_rule(
+    implementation = _hscloud_gen_pip_imports_impl,
+    attrs = dict(),
+)
+
+def hscloud_setup_nix(revision, sha256):
+    rules_nixpkgs_dependencies()
+    nixpkgs_git_repository(
+        name = "nixpkgs",
+        revision = "1179840f9a88b8a548f4b11d1a03aa25a790c379",
+        sha256 = "8b64041bfb9760de9e797c0a985a4830880c21732489f397e217d877edd9a990",
+    )
+
+    # Load python from nixpkgs. Python is a large source of non-hermiticity,
+    # and loading it from nix vastly hermeticizes the build - well, at least to
+    # also be dependent on this Nix store state. That's still better than just
+    # grabbing whatever random system Python a user might have.
+    nixpkgs_package(
+        name = "hscloud_nix_python2",
+        repositories = { "nixpkgs": "@nixpkgs//:default.nix" },
+        nix_file = "//third_party/nix:python.nix",
+        attribute_path = "python2",
+        build_file_content = """
+package(default_visibility = ["//visibility:public"])
+exports_files(["python2"])
+        """,
+    )
+    nixpkgs_package(
+        name = "hscloud_nix_python3",
+        repositories = { "nixpkgs": "@nixpkgs//:default.nix" },
+        nix_file = "//third_party/nix:python.nix",
+        attribute_path = "python3",
+        build_file_content = """
+package(default_visibility = ["//visibility:public"])
+exports_files(["python3"])
+        """,
+    )
+
+    # Generate a Go toolchain setup workspace rule.
+    hscloud_gen_go_imports(
+        name = "hscloud_go_toolchain",
+    )
+    hscloud_gen_pip_imports(
+        name = "hscloud_pip_imports",
+    )
diff --git a/third_party/oniguruma/BUILD b/third_party/oniguruma/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/oniguruma/BUILD
diff --git a/third_party/oniguruma/BUILD.external b/third_party/oniguruma/BUILD.external
new file mode 100644
index 0000000..b10f673
--- /dev/null
+++ b/third_party/oniguruma/BUILD.external
@@ -0,0 +1,441 @@
+# Copyright 2019 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The Oniguruma library.
+cc_library(
+    name = "oniguruma",
+    hdrs = [
+        "src/oniguruma.h",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":ascii",
+        ":big5",
+        ":cp1251",
+        ":euc_jp",
+        ":euc_kr",
+        ":euc_tw",
+        ":gb18030",
+        ":iso8859",
+        ":koi8",
+        ":onig_init",
+        ":regcomp",
+        ":regenc",
+        ":regerror",
+        ":regexec",
+        ":regext",
+        ":reggnu",
+        ":regparse",
+        ":regposix",
+        ":regsyntax",
+        ":regtrav",
+        ":regversion",
+        ":sjis",
+        ":st",
+        ":unicode",
+        ":utf16",
+        ":utf32",
+        ":utf8",
+    ],
+)
+
+# The mktable binary.
+cc_binary(
+    name = "mktable",
+    srcs = [
+        "src/mktable.c",
+    ],
+    deps = [
+        ":oniguruma",
+    ],
+)
+
+# Other libraries linked in with :oniguruma.
+cc_library(
+    name = "ascii",
+    srcs = [
+        "src/ascii.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "big5",
+    srcs = [
+        "src/big5.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "cp1251",
+    srcs = [
+        "src/cp1251.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "euc_jp",
+    srcs = [
+        "src/euc_jp.c",
+        "src/euc_jp_prop.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "euc_kr",
+    srcs = [
+        "src/euc_kr.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "euc_tw",
+    srcs = [
+        "src/euc_tw.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "gb18030",
+    srcs = [
+        "src/gb18030.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "iso8859",
+    srcs = [
+        "src/iso8859_1.c",
+        "src/iso8859_10.c",
+        "src/iso8859_11.c",
+        "src/iso8859_13.c",
+        "src/iso8859_14.c",
+        "src/iso8859_15.c",
+        "src/iso8859_16.c",
+        "src/iso8859_2.c",
+        "src/iso8859_3.c",
+        "src/iso8859_4.c",
+        "src/iso8859_5.c",
+        "src/iso8859_6.c",
+        "src/iso8859_7.c",
+        "src/iso8859_8.c",
+        "src/iso8859_9.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "koi8",
+    srcs = [
+        "src/koi8.c",
+        "src/koi8_r.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "onig_init",
+    srcs = [
+        "src/onig_init.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regcomp",
+    srcs = [
+        "src/regcomp.c",
+    ],
+    deps = [
+        ":regparse",
+    ],
+    alwayslink = 1,
+    copts = [
+        "-Wno-maybe-uninitialized",
+    ],
+)
+
+cc_library(
+    name = "regenc",
+    srcs = [
+        "src/regenc.c",
+    ],
+    hdrs = [
+        "src/oniguruma.h",
+        "src/regenc.h",
+        "src/regint.h",
+    ],
+    deps = [
+        ":config",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regerror",
+    srcs = [
+        "src/regerror.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regexec",
+    srcs = [
+        "src/regexec.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regext",
+    srcs = [
+        "src/regext.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "reggnu",
+    srcs = [
+        "src/reggnu.c",
+    ],
+    hdrs = [
+        "src/oniggnu.h",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regparse",
+    srcs = [
+        "src/regparse.c",
+    ],
+    hdrs = [
+        "src/regparse.h",
+    ],
+    deps = [
+        ":st",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regposix",
+    srcs = [
+        "src/regposerr.c",
+        "src/regposix.c",
+    ],
+    hdrs = [
+        "src/onigposix.h",
+    ],
+    deps = [
+        ":config",
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regsyntax",
+    srcs = [
+        "src/regsyntax.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regtrav",
+    srcs = [
+        "src/regtrav.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "regversion",
+    srcs = [
+        "src/regversion.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "sjis",
+    srcs = [
+        "src/sjis.c",
+        "src/sjis_prop.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "st",
+    srcs = [
+        "src/st.c",
+    ],
+    hdrs = [
+        "src/st.h",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "unicode",
+    srcs = [
+        "src/unicode.c",
+        "src/unicode_fold1_key.c",
+        "src/unicode_fold2_key.c",
+        "src/unicode_fold3_key.c",
+        "src/unicode_unfold_key.c",
+    ],
+    deps = [
+        ":config",
+        ":data",
+        ":regenc",
+        ":st",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "utf8",
+    srcs = [
+        "src/utf8.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "utf16",
+    srcs = [
+        "src/utf16_be.c",
+        "src/utf16_le.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "utf32",
+    srcs = [
+        "src/utf32_be.c",
+        "src/utf32_le.c",
+    ],
+    deps = [
+        ":regenc",
+    ],
+    alwayslink = 1,
+)
+
+# Generated data files.
+cc_library(
+    name = "data",
+    hdrs = [
+        "src/unicode_egcb_data.c",
+        "src/unicode_fold_data.c",
+        "src/unicode_property_data.c",
+        "src/unicode_property_data_posix.c",
+        "src/unicode_wb_data.c",
+    ],
+)
+
+# CC library containing config.h.
+cc_library(
+    name = "config",
+    hdrs = [
+        ":config_h",
+    ],
+)
+
+genrule(
+    name = "config_h",
+    srcs = glob(["**"]),
+    outs = ["config.h"],
+    cmd =
+        "( " +
+        "  cd external/com_github_kkos_oniguruma; " +
+        "  ./configure > /dev/null; " +
+        "); " +
+        "cp external/com_github_kkos_oniguruma/src/config.h \"$@\"; ",
+)
+
diff --git a/third_party/oniguruma/README.md b/third_party/oniguruma/README.md
new file mode 100644
index 0000000..eb0d88c
--- /dev/null
+++ b/third_party/oniguruma/README.md
@@ -0,0 +1,6 @@
+third\_party: oniguruma
+=======================
+
+Upstream: https://github.com/kkos/oniguruma
+
+Pulled in by WORKSPACE (com\_github\_kkos\_oniguruma), BUILD.external used as root BUILDfile for external repository.
diff --git a/third_party/py/BUILD b/third_party/py/BUILD
index 56b5534..e69de29 100644
--- a/third_party/py/BUILD
+++ b/third_party/py/BUILD
@@ -1,7 +0,0 @@
-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
deleted file mode 100644
index a3296d4..0000000
--- a/third_party/py/requirements-lock.json
+++ /dev/null
@@ -1,528 +0,0 @@
-{
-  "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"
-        },
-        "blinker": {
-          "dependencies": [],
-          "is_direct": true,
-          "source": "blinker_1_4_py3_none_any",
-          "version": "1.4"
-        },
-        "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"
-        },
-        "cockroachdb": {
-          "dependencies": [],
-          "is_direct": true,
-          "source": "cockroachdb_0_3_3_py3_none_any",
-          "version": "0.3.3"
-        },
-        "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-login": {
-          "dependencies": [
-            "flask"
-          ],
-          "is_direct": true,
-          "source": "Flask_Login_0_4_1_py2_py3_none_any",
-          "version": "0.4.1"
-        },
-        "flask-oauthlib": {
-          "dependencies": [
-            "flask",
-            "oauthlib",
-            "requests-oauthlib"
-          ],
-          "is_direct": true,
-          "source": "Flask_OAuthlib_0_9_5_py3_none_any",
-          "version": "0.9.5"
-        },
-        "flask-sqlalchemy": {
-          "dependencies": [
-            "flask",
-            "sqlalchemy"
-          ],
-          "is_direct": true,
-          "source": "Flask_SQLAlchemy_2_4_0_py2_py3_none_any",
-          "version": "2.4.0"
-        },
-        "flask-wtf": {
-          "dependencies": [
-            "flask",
-            "wtforms"
-          ],
-          "is_direct": true,
-          "source": "Flask_WTF_0_14_2_py2_py3_none_any",
-          "version": "0.14.2"
-        },
-        "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"
-        },
-        "oauthlib": {
-          "dependencies": [],
-          "is_direct": true,
-          "source": "oauthlib_2_1_0_py2_py3_none_any",
-          "version": "2.1.0"
-        },
-        "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"
-        },
-        "psycopg2": {
-          "dependencies": [],
-          "is_direct": true,
-          "source": "psycopg2_2_7_7_cp36_cp36m_manylinux1_x86_64",
-          "version": "2.7.7"
-        },
-        "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"
-        },
-        "requests-oauthlib": {
-          "dependencies": [
-            "oauthlib",
-            "requests"
-          ],
-          "is_direct": true,
-          "source": "requests_oauthlib_1_3_0_py2_py3_none_any",
-          "version": "1.3.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"
-        },
-        "wtforms": {
-          "dependencies": [],
-          "is_direct": true,
-          "source": "WTForms_2_2_1_py2_py3_none_any",
-          "version": "2.2.1"
-        }
-      },
-      "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_Login_0_4_1_py2_py3_none_any": {
-      "file": "Flask_Login-0.4.1-py2.py3-none-any.whl"
-    },
-    "Flask_OAuthlib_0_9_5_py3_none_any": {
-      "file": "Flask_OAuthlib-0.9.5-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"
-    },
-    "Flask_WTF_0_14_2_py2_py3_none_any": {
-      "sha256": "d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac",
-      "url": "https://files.pythonhosted.org/packages/60/3a/58c629472d10539ae5167dc7c1fecfa95dd7d0b7864623931e3776438a24/Flask_WTF-0.14.2-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"
-    },
-    "WTForms_2_2_1_py2_py3_none_any": {
-      "sha256": "e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1",
-      "url": "https://files.pythonhosted.org/packages/9f/c8/dac5dce9908df1d9d48ec0e26e2a250839fa36ea2c602cc4f85ccfeb5c65/WTForms-2.2.1-py2.py3-none-any.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"
-    },
-    "blinker_1_4_py3_none_any": {
-      "file": "blinker-1.4-py3-none-any.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"
-    },
-    "cockroachdb_0_3_3_py3_none_any": {
-      "file": "cockroachdb-0.3.3-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"
-    },
-    "oauthlib_2_1_0_py2_py3_none_any": {
-      "sha256": "d883b36b21a6ad813953803edfa563b1b579d79ca758fe950d1bc9e8b326025b",
-      "url": "https://files.pythonhosted.org/packages/e6/d1/ddd9cfea3e736399b97ded5c2dd62d1322adef4a72d816f1ed1049d6a179/oauthlib-2.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"
-    },
-    "psycopg2_2_7_7_cp36_cp36m_manylinux1_x86_64": {
-      "sha256": "ed7e0849337bd37d89f2c2b0216a0de863399ee5d363d31b1e5330a99044737b",
-      "url": "https://files.pythonhosted.org/packages/37/25/53e8398975aa3323de46a5cc2745aeb4c9db11352ca905d3a15c53b6a816/psycopg2-2.7.7-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"
-    },
-    "requests_oauthlib_1_3_0_py2_py3_none_any": {
-      "sha256": "7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
-      "url": "https://files.pythonhosted.org/packages/a3/12/b92740d845ab62ea4edf04d2f4164d82532b5a0b03836d4d4e71c6f3d379/requests_oauthlib-1.3.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/third_party/py/requirements.txt b/third_party/py/requirements.txt
index 02ed586..94765d7 100644
--- a/third_party/py/requirements.txt
+++ b/third_party/py/requirements.txt
@@ -1,9 +1,12 @@
+# grpcio and protobuf are installed directly via WORKSPACE
+# do NOT add them there
+# depending on a py_grpc_library output will pull in the required deps
 arrow==0.14.5
 asn1crypto==0.24.0
 bcrypt==3.1.5
 blinker==1.4
 certifi==2019.6.16
-cffi==1.11.5
+cffi==1.14.1
 chardet==3.0.4
 Click==7.0
 cockroachdb==0.3.3
@@ -18,7 +21,6 @@
 future==0.17.1
 gevent==1.4.0
 greenlet==0.4.15
-grpcio==1.22.0
 gunicorn==19.9.0
 idna==2.8
 invoke==1.2.0
@@ -27,11 +29,11 @@
 MarkupSafe==1.1.1
 oauthlib==2.1.0
 paramiko==2.4.2
-protobuf==3.9.0
-psycopg2==2.7.7
+psycopg2==2.8.5
 pyasn1==0.4.5
 pycparser==2.19
 PyNaCl==1.3.0
+pyelftools==0.26
 python-dateutil==2.8.0
 pytz==2019.1
 requests==2.22.0
diff --git a/third_party/py/wheels/BUILD b/third_party/py/wheels/BUILD
deleted file mode 100644
index 1b09257..0000000
--- a/third_party/py/wheels/BUILD
+++ /dev/null
@@ -1 +0,0 @@
-# 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/Flask_Login-0.4.1-py2.py3-none-any.whl b/third_party/py/wheels/Flask_Login-0.4.1-py2.py3-none-any.whl
deleted file mode 100644
index f200a54..0000000
--- a/third_party/py/wheels/Flask_Login-0.4.1-py2.py3-none-any.whl
+++ /dev/null
Binary files differ
diff --git a/third_party/py/wheels/Flask_OAuthlib-0.9.5-py3-none-any.whl b/third_party/py/wheels/Flask_OAuthlib-0.9.5-py3-none-any.whl
deleted file mode 100644
index 30fba3d..0000000
--- a/third_party/py/wheels/Flask_OAuthlib-0.9.5-py3-none-any.whl
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index b54494b..0000000
--- a/third_party/py/wheels/SQLAlchemy-1.3.8-cp36-cp36m-linux_x86_64.whl
+++ /dev/null
Binary files differ
diff --git a/third_party/py/wheels/blinker-1.4-py3-none-any.whl b/third_party/py/wheels/blinker-1.4-py3-none-any.whl
deleted file mode 100644
index b2d4d59..0000000
--- a/third_party/py/wheels/blinker-1.4-py3-none-any.whl
+++ /dev/null
Binary files differ
diff --git a/third_party/py/wheels/cockroachdb-0.3.3-py3-none-any.whl b/third_party/py/wheels/cockroachdb-0.3.3-py3-none-any.whl
deleted file mode 100644
index f40f02b..0000000
--- a/third_party/py/wheels/cockroachdb-0.3.3-py3-none-any.whl
+++ /dev/null
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
deleted file mode 100644
index 77f89e3..0000000
--- a/third_party/py/wheels/future-0.17.1-py3-none-any.whl
+++ /dev/null
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
deleted file mode 100644
index f00822f..0000000
--- a/third_party/py/wheels/pycparser-2.19-py2.py3-none-any.whl
+++ /dev/null
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
deleted file mode 100644
index 1bf3680..0000000
--- a/third_party/py/wheels/uWSGI-2.0.18-cp36-cp36m-linux_x86_64.whl
+++ /dev/null
Binary files differ
diff --git a/tools/go_sdk.bzl b/tools/go_sdk.bzl
deleted file mode 100644
index 3513b75..0000000
--- a/tools/go_sdk.bzl
+++ /dev/null
@@ -1,31 +0,0 @@
-def _gen_imports_impl(ctx):
-    ctx.file("BUILD", "")
-
-    if "hscloud_root" not in ctx.os.environ:
-        fail("Please souce env.sh")
-
-    is_nixos = "hscloud_nixos" in ctx.os.environ
-    bzl_file_content = """
-load(
-    "@io_bazel_rules_go//go:deps.bzl",
-    "go_register_toolchains",
-    "go_rules_dependencies",
-)
-def load_go_sdk():
-    go_rules_dependencies()
-    go_register_toolchains({go_version})
-    """.format(
-        go_version = 'go_version = "host"' if is_nixos else "",
-    )
-
-    ctx.file("imports.bzl", bzl_file_content)
-
-_gen_imports = repository_rule(
-    implementation = _gen_imports_impl,
-    attrs = dict(),
-)
-
-def gen_imports(name):
-    _gen_imports(
-        name = name,
-    )
diff --git a/tools/secretstore.py b/tools/secretstore.py
index 55af48c..b0d2cfe 100644
--- a/tools/secretstore.py
+++ b/tools/secretstore.py
@@ -1,21 +1,63 @@
 #!/usr/bin/env python3
 
-# A little tool to encrypt/decrypt git secrets. Kinda like password-store, but more purpose specific and portable.
+"""
+A little tool to encrypt/decrypt git secrets. Kinda like password-store, but
+more purpose specific and portable.
 
+It generally expects to work with directory structures as follows:
+
+    foo/bar/secrets/plain:  plaintext files
+                   /cipher: ciphertext files, with names corresponding to
+                            plaintext files
+
+Note: currently all plaintext/cipher files are at a single level, ie.: there
+cannot be any subdirectory within a /plain or /cipher directory.
+
+There are multiple secret 'roots' like this in hscloud, notably:
+
+ - cluster/secrets
+ - hswaw/kube/secrets
+
+In the future, some repository-based configuration might exist to specify these
+roots in a nicer way, possibly with different target keys per root.
+
+This tool a bit of a swiss army knife, and can be used in the following ways:
+
+ - as a CLI tool to encrypt/decrypt files directly
+ - as a library for its encryption/decryption methods, and for a SecretStore
+   API, which allows for basic programmatic access to secrets, decrypting
+   things if necessary
+ - as a CLI tool to 'synchronize' a directory containing plain/cipher files,
+   which means encrypting every new plaintext file (or new ciphertext file),
+   and re-encrypting all files whose keys are different from the keys list
+   defined in this file.
+
+"""
+
+import argparse
 import logging
 import os
 import sys
 import subprocess
+import tempfile
 
+# Keys that are to be used to encrypt all secret roots.
 keys = [
     "63DFE737F078657CC8A51C00C29ADD73B3563D82", # q3k
     "482FF104C29294AD1CAF827BA43890A3DE74ECC7", # inf
-    # "F07205946C07EEB2041A72FBC60C64879534F768", # cz2 (expired 2020-01-17)
+    "F07205946C07EEB2041A72FBC60C64879534F768", # cz2
     "0879F9FCA1C836677BB808C870FD60197E195C26", # implr
 ]
 
 
-logger = logging.getLogger(__name__)
+_logger_name = __name__
+if _logger_name == '__main__':
+    _logger_name = 'secretstore'
+logger = logging.getLogger(_logger_name)
+
+
+class CLIException(Exception):
+    pass
 
 
 def encrypt(src, dst):
@@ -26,9 +68,289 @@
     cmd.append(src)
     subprocess.check_call(cmd)
 
+
 def decrypt(src, dst):
     cmd = ['gpg', '--decrypt', '--batch', '--yes', '--output', dst, src]
-    subprocess.check_call(cmd)
+    # catch stdout to make this code less chatty.
+    subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+
+
+def _encryption_key_for_fingerprint(fp):
+    """
+    Returns the encryption key ID for a given GPG fingerprint (eg. one from the
+    'keys' list.
+    """
+    cmd = ['gpg', '-k', '--keyid-format', 'long', fp]
+    res = subprocess.check_output(cmd).decode()
+
+    # Sample output:
+    #   pub   rsa4096/70FD60197E195C26 2014-02-22 [SC] [expires: 2021-02-05]
+    #         0879F9FCA1C836677BB808C870FD60197E195C26
+    #   uid                 [ultimate] Bartosz Stebel <bartoszstebel@gmail.com>
+    #   uid                 [ultimate] Bartosz Stebel <implr@hackerspace.pl>
+    #   sub   rsa4096/E203C94E5CEBB3EF 2014-02-22 [E] [expires: 2021-02-05]
+    #
+    # We want to extract the 'sub' key with the [E] tag.
+    for line in res.split('\n'):
+        line = line.strip()
+        if not line:
+            continue
+        parts = line.split()
+        if len(parts) < 4:
+            continue
+        if parts[0] != 'sub':
+            continue
+
+        if not parts[3].startswith('[') or not parts[3].endswith(']'):
+            continue
+        usages = parts[3].strip('[]')
+        if 'E' not in usages:
+            continue
+
+        # Okay, we found the encryption key.
+        return parts[1].split('/')[1]
+
+    raise Exception("Could not find encryption key ID for fingerprint {}".format(fp))
+
+
+_encryption_keys_cache = None
+def encryption_keys():
+    """
+    Return all encryption keys associated with the keys array.
+    """
+    global _encryption_keys_cache
+    if _encryption_keys_cache is None:
+        _encryption_keys_cache = [_encryption_key_for_fingerprint(fp) for fp in keys]
+
+    return _encryption_keys_cache
+
+
+def encrypted_for(path):
+    """
+    Return for which encryption keys is a given GPG ciphertext file encrypted.
+    """
+    cmd = ['gpg', '--pinentry-mode', 'cancel', '--list-packets', path]
+    res = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode()
+
+    # Sample output:
+    #  gpg: encrypted with 4096-bit RSA key, ID E203C94E5CEBB3EF, created 2014-02-22
+    #        "Bartosz Stebel <bartoszstebel@gmail.com>"
+    #  gpg: encrypted with 2048-bit RSA key, ID 5C1B6B69E9F5EABE, created 2013-01-29
+    #        "Piotr Dobrowolski <piotr.tytus.dobrowolski@gmail.com>"
+    #  gpg: encrypted with 2048-bit RSA key, ID 386E893E110BC55B, created 2012-01-10
+    #        "Sergiusz Bazanski (Low Latency Consulting) <serge@lowlatency.ie>"
+    #  gpg: public key decryption failed: Operation cancelled
+    #  gpg: decryption failed: No secret key
+    #  # off=0 ctb=85 tag=1 hlen=3 plen=268
+    #  :pubkey enc packet: version 3, algo 1, keyid 386E893E110BC55B
+    #  	data: [2047 bits]
+    #  # off=271 ctb=85 tag=1 hlen=3 plen=268
+    #  :pubkey enc packet: version 3, algo 1, keyid 5C1B6B69E9F5EABE
+    #  	data: [2048 bits]
+    #  # off=542 ctb=85 tag=1 hlen=3 plen=524
+    #  :pubkey enc packet: version 3, algo 1, keyid E203C94E5CEBB3EF
+    #  	data: [4095 bits]
+    #  # off=1069 ctb=d2 tag=18 hlen=2 plen=121 new-ctb
+    #  :encrypted data packet:
+    #  	length: 121
+    #  	mdc_method: 2
+
+    keys = []
+    for line in res.split('\n'):
+        line = line.strip()
+        if not line:
+            continue
+
+        parts = line.split()
+        if len(parts) < 9:
+            continue
+
+
+        if parts[:4] != [':pubkey', 'enc', 'packet:', 'version']:
+            continue
+
+        if parts[7] != 'keyid':
+            continue
+
+        keys.append(parts[8])
+
+    # Make unique.
+    return list(set(keys))
+
+
+class SyncAction:
+    """
+    SyncAction is a possible action taken to synchronize some secrets.
+
+    An action is some sort of side-effect bearing OS action (ie execution of
+    script or file move, or...) that can also 'describe' that it's acting - ie,
+    just return a human readable string of what it would be doing. These
+    describe descriptions are used for dry-runs of the secretstore sync
+    functionality.
+    """
+    def describe(self):
+        return ""
+
+    def act(self):
+        pass
+
+class SyncActionEncrypt:
+    def __init__(self, src, dst, reason):
+        self.src = src
+        self.dst = dst
+        self.reason = reason
+
+    def describe(self):
+        return f'Encrypting {os.path.split(self.src)[-1]} ({self.reason})'
+
+    def act(self):
+        return encrypt(self.src, self.dst)
+
+
+class SyncActionDecrypt:
+    def __init__(self, src, dst, reason):
+        self.src = src
+        self.dst = dst
+        self.reason = reason
+
+    def describe(self):
+        return f'Decrypting {os.path.split(self.src)[-1]} ({self.reason})'
+
+    def act(self):
+        return decrypt(self.src, self.dst)
+
+
+def sync(path: str, dry: bool):
+    """Synchronize (decrypt and encrypt what's needed) a given secrets directory."""
+
+    # Turn the path into an absolute path just to make things safer.
+    path = os.path.abspath(path)
+    # Trim all trailing slashes to canonicalize.
+    path = path.rstrip('/')
+
+    plain_path = os.path.join(path, "plain")
+    cipher_path = os.path.join(path, "cipher")
+
+    # Ensure that at least one of the plain/cipher paths exist.
+    plain_exists = os.path.exists(plain_path)
+    cipher_exists = os.path.exists(cipher_path)
+    if not plain_exists and not cipher_exists:
+        raise CLIException('Given directory must contain a plain/ or cipher/ subdirectory.')
+
+    # Make missing directories.
+    if not plain_exists:
+        os.mkdir(plain_path)
+    if not cipher_exists:
+        os.mkdir(cipher_path)
+
+    # List files on both sides:
+    plain_files = [f for f in os.listdir(plain_path) if f != '.gitignore' and os.path.isfile(os.path.join(plain_path, f))]
+    cipher_files = [f for f in os.listdir(cipher_path) if os.path.isfile(os.path.join(cipher_path, f))]
+
+    # Helper function to turn a short filename within a directory to a pair
+    # of plain/cipher full paths.
+    def pc(p):
+        return os.path.join(plain_path, p), os.path.join(cipher_path, p)
+
+    # Make a set of all file names - no matter if only available as plain, as
+    # cipher, or as both.
+    all_files = set(plain_files + cipher_files)
+
+    # We'll be making a list of actions to perform to bring up given directory
+    # pair to a stable state.
+    actions = []  # type: List[SyncAction]
+
+    # First, for every possible file (either encrypted or decrypted), figure
+    # out which side is fresher based on file presence and mtime.
+    fresher = {}  # type: Dict[str, str]
+    for p in all_files:
+        # Handle the easy case when the file only exists on one side.
+        if p not in cipher_files:
+            fresher[p] = 'plain'
+            continue
+        if p not in plain_files:
+            fresher[p] = 'cipher'
+            continue
+
+        plain, cipher = pc(p)
+
+        # Otherwise, we have both the cipher and plain version.
+        # Check if the decrypted version matches the plaintext version. If so,
+        # they're both equal.
+
+        f = tempfile.NamedTemporaryFile(delete=False)
+        f.close()
+        decrypt(cipher, f.name)
+
+        with open(f.name, 'rb') as fd:
+            decrypted_data = fd.read()
+        with open(plain, 'rb') as fc:
+            current_data = fc.read()
+
+        if decrypted_data == current_data:
+            fresher[p] = 'equal'
+            os.unlink(f.name)
+            continue
+
+        os.unlink(f.name)
+
+        # The plain and cipher versions differ. Let's choose based on mtime.
+        mtime_plain = os.path.getmtime(plain)
+        mtime_cipher = os.path.getmtime(cipher)
+
+        if mtime_plain > mtime_cipher:
+            fresher[p] = 'plain'
+        elif mtime_cipher > mtime_plain:
+            fresher[p] = 'cipher'
+        else:
+            raise CLIException(f'cipher/plain stalemate on {p}: contents differ, but files have same mtime')
+
+    # Find all files that need to be re-encrypted for changed keys.
+    reencrypt = set()
+    for p in cipher_files:
+        _, cipher = pc(p)
+        current = set(encrypted_for(cipher))
+        want = set(encryption_keys())
+
+        if current != want:
+            reencrypt.add(p)
+
+    # Okay, now actually construct a list of actions.
+    # First, all fresh==cipher keys need to be decrypted.
+    for p, v in fresher.items():
+        if v != 'cipher':
+            continue
+
+        plain, cipher = pc(p)
+        actions.append(SyncActionDecrypt(cipher, plain, "cipher version is newer"))
+
+    encrypted = set()
+    # Then, encrypt all fresh==plain files, and make note of what those
+    # are.
+    for p, v in fresher.items():
+        if v != 'plain':
+            continue
+
+        plain, cipher = pc(p)
+        actions.append(SyncActionEncrypt(plain, cipher, "plain version is newer"))
+        encrypted.add(p)
+
+    # Finally, re-encrypt all the files that aren't already being encrypted.
+    for p in reencrypt.difference(encrypted):
+        plain, cipher = pc(p)
+        actions.append(SyncActionEncrypt(plain, cipher, "needs to be re-encrypted for different keys"))
+
+    if len(actions) == 0:
+        logger.info('Nothing to do!')
+    else:
+        if dry:
+            logger.info('Would perform the following:')
+        else:
+            logger.info('Running actions...')
+    for a in actions:
+        logger.info(a.describe())
+        if not dry:
+            a.act()
 
 
 class SecretStoreMissing(Exception):
@@ -75,18 +397,43 @@
 
 
 def main():
-    if len(sys.argv) < 3 or sys.argv[1] not in ('encrypt', 'decrypt'):
-        sys.stderr.write("Usage: {} encrypt/decrypt file\n".format(sys.argv[0]))
-        sys.stderr.flush()
-        return 1
+    parser = argparse.ArgumentParser(description='Manage hscloud git-based secrets.')
+    subparsers = parser.add_subparsers(dest='mode')
 
-    action = sys.argv[1]
-    src = sys.argv[2]
+    parser_decrypt = subparsers.add_parser('decrypt', help='decrypt a single secret file')
+    parser_decrypt.add_argument('input', type=str, help='encrypted file path')
+    parser_decrypt.add_argument('output', type=str, default='-', help='decrypted file path file path (or - for stdout)')
 
-    if action == 'encrypt':
-        encrypt(src, '-')
-    else:
-        decrypt(src, '-')
+    parser_encrypt = subparsers.add_parser('encrypt', help='encrypt a single secret file')
+    parser_encrypt.add_argument('input', type=str, help='plaintext file path')
+    parser_encrypt.add_argument('output', type=str, default='-', help='encrypted file path file path (or - for stdout)')
+
+    parser_sync = subparsers.add_parser('sync', help='Synchronize a canonically formatted secrets/{plain,cipher} directory')
+    parser_sync.add_argument('dir', type=str, help='Path to secrets directory to synchronize')
+    parser_sync.add_argument('--dry', dest='dry', action='store_true')
+    parser_sync.set_defaults(dry=False)
+
+    logging.basicConfig(level='INFO')
+
+    args = parser.parse_args()
+
+    if args.mode == None:
+        parser.print_help()
+        sys.exit(1)
+
+    try:
+        if args.mode == 'encrypt':
+            encrypt(args.input, args.output)
+        elif args.mode == 'decrypt':
+            decrypt(args.input, args.output)
+        elif args.mode == 'sync':
+            sync(args.dir, dry=args.dry)
+        else:
+            # ???
+            raise Exception('invalid mode {}'.format(args.mode))
+    except CLIException as e:
+        logger.error(e)
+        sys.exit(1)
 
 if __name__ == '__main__':
     sys.exit(main() or 0)