diff --git a/src/dispatcher.cpp b/src/dispatcher.cpp
index 57c9475f516496bdcab197e0f3936fb5bf4a2a9f..e668d1ee1f705c11880cbc5dbe127a61b3e69ef9 100644
--- a/src/dispatcher.cpp
+++ b/src/dispatcher.cpp
@@ -34,6 +34,7 @@ std::string object_type_to_string(const msgpack::type::object_type t) {
 }
 
 vector<string> Dispatcher::getBindings() const {
+    SHARED_LOCK(mutex_, lk);
     vector<string> res;
     for (auto x : funcs_) {
         res.push_back(x.first);
@@ -94,6 +95,7 @@ void ftl::net::Dispatcher::dispatch_call(Peer &s, const msgpack::object &msg) {
 }
 
 optional<Dispatcher::adaptor_type> ftl::net::Dispatcher::_locateHandler(const std::string &name) const {
+    SHARED_LOCK(mutex_, lk);
     auto it_func = funcs_.find(name);
     if (it_func == funcs_.end()) {
         if (parent_ != nullptr) {
@@ -107,6 +109,7 @@ optional<Dispatcher::adaptor_type> ftl::net::Dispatcher::_locateHandler(const st
 }
 
 bool ftl::net::Dispatcher::isBound(const std::string &name) const {
+    SHARED_LOCK(mutex_, lk);
     return funcs_.find(name) != funcs_.end();
 }
 
@@ -151,6 +154,7 @@ void ftl::net::Dispatcher::enforce_arg_count(std::string const &func, std::size_
 }
 
 void ftl::net::Dispatcher::enforce_unique_name(std::string const &func) {
+    SHARED_LOCK(mutex_, lk);
     auto pos = funcs_.find(func);
     if (pos != end(funcs_)) {
         throw FTL_Error("RPC non unique binding for '" << func << "'");
diff --git a/src/dispatcher.hpp b/src/dispatcher.hpp
index c7f89274ff364e40a59aaefe2ec99cd899afc2a8..333c77de26a0906b5bd6cb0d965f07325e56bbab 100644
--- a/src/dispatcher.hpp
+++ b/src/dispatcher.hpp
@@ -18,6 +18,8 @@
 
 #include "func_traits.hpp"
 
+#include <ftl/threads.hpp>
+
 #include <msgpack.hpp>
 
 namespace ftl {
@@ -70,10 +72,7 @@ namespace net {
  */
 class Dispatcher {
  public:
-    explicit Dispatcher(Dispatcher *parent = nullptr) : parent_(parent) {
-        // FIXME: threading and funcs_; hack use large size
-        funcs_.reserve(1024);
-    }
+    explicit Dispatcher(Dispatcher *parent = nullptr) : parent_(parent) {}
 
     /**
      * Primary method by which a peer dispatches a msgpack object that this
@@ -94,6 +93,7 @@ class Dispatcher {
                           ftl::internal::tags::zero_arg const &,
                           ftl::internal::false_ const &) {
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(
             std::make_pair(name, [func, name](ftl::net::Peer &p, msgpack::object const &args) {
                 enforce_arg_count(name, 0, args.via.array.size);
@@ -116,6 +116,7 @@ class Dispatcher {
         using args_type = typename func_traits<F>::args_type;
 
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(
             std::make_pair(name, [func, name](ftl::net::Peer &p, msgpack::object const &args) {
                 constexpr int args_count = std::tuple_size<args_type>::value;
@@ -140,6 +141,7 @@ class Dispatcher {
         using ftl::internal::func_traits;
 
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(std::make_pair(name, [func,
                                             name](ftl::net::Peer &p, msgpack::object const &args) {
             enforce_arg_count(name, 0, args.via.array.size);
@@ -163,6 +165,7 @@ class Dispatcher {
         using args_type = typename func_traits<F>::args_type;
 
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(std::make_pair(name, [func,
                                             name](ftl::net::Peer &p, msgpack::object const &args) {
             constexpr int args_count = std::tuple_size<args_type>::value;
@@ -183,6 +186,7 @@ class Dispatcher {
                           ftl::internal::tags::zero_arg const &,
                           ftl::internal::true_ const &) {
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(
             std::make_pair(name, [func, name](ftl::net::Peer &p, msgpack::object const &args) {
                 enforce_arg_count(name, 0, args.via.array.size);
@@ -200,6 +204,7 @@ class Dispatcher {
         using args_type = typename func_traits<F>::args_type;
 
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(
             std::make_pair(name, [func, name](ftl::net::Peer &p, msgpack::object const &args) {
                 constexpr int args_count = std::tuple_size<args_type>::value;
@@ -219,6 +224,7 @@ class Dispatcher {
         using ftl::internal::func_traits;
 
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(std::make_pair(name, [func,
                                             name](ftl::net::Peer &p, msgpack::object const &args) {
             enforce_arg_count(name, 0, args.via.array.size);
@@ -237,6 +243,7 @@ class Dispatcher {
         using args_type = typename func_traits<F>::args_type;
 
         enforce_unique_name(name);
+        UNIQUE_LOCK(mutex_, lk);
         funcs_.insert(std::make_pair(name, [func,
                                             name](ftl::net::Peer &p, msgpack::object const &args) {
             constexpr int args_count = std::tuple_size<args_type>::value;
@@ -255,6 +262,7 @@ class Dispatcher {
      * Remove a previous bound function by name.
      */
     void unbind(const std::string &name) {
+        UNIQUE_LOCK(mutex_, lk);
         auto i = funcs_.find(name);
         if (i != funcs_.end()) {
             funcs_.erase(i);
@@ -290,6 +298,7 @@ class Dispatcher {
  private:
     Dispatcher *parent_;
     std::unordered_map<std::string, adaptor_type> funcs_;
+    mutable SHARED_MUTEX mutex_;
 
     std::optional<adaptor_type> _locateHandler(const std::string &name) const;