From cc2950e247808a03eb82c2045e37807c1ee761e6 Mon Sep 17 00:00:00 2001
From: iphydf <iphydf@users.noreply.github.com>
Date: Mon, 25 Mar 2024 18:46:58 +0000
Subject: [PATCH] refactor: Move some OS-specifics into tox_system.

---
 BUILD.bazel                                   |  68 ++-
 CMakeLists.txt                                |  62 ++-
 auto_tests/BUILD.bazel                        |   5 +
 auto_tests/TCP_test.c                         |  16 +-
 auto_tests/announce_test.c                    |   5 +-
 auto_tests/auto_test_support.c                |  18 +-
 auto_tests/auto_test_support.h                |   1 +
 auto_tests/conference_av_test.c               |   1 +
 auto_tests/conference_test.c                  |   1 +
 auto_tests/crypto_test.c                      |   3 +
 auto_tests/encryptsave_test.c                 |   1 +
 auto_tests/forwarding_test.c                  |   5 +-
 auto_tests/group_message_test.c               |   1 +
 auto_tests/group_sync_test.c                  |   1 +
 auto_tests/group_topic_test.c                 |   3 +-
 auto_tests/lan_discovery_test.c               |  18 +-
 auto_tests/network_test.c                     |   2 +
 auto_tests/onion_test.c                       |  11 +-
 auto_tests/reconnect_test.c                   |   1 +
 auto_tests/save_friend_test.c                 |   1 +
 auto_tests/save_load_test.c                   |  23 +-
 auto_tests/tox_events_test.c                  |  17 +-
 auto_tests/tox_many_tcp_test.c                |   1 +
 auto_tests/tox_many_test.c                    |   1 +
 auto_tests/toxav_many_test.c                  |  28 +-
 cmake/ModulePackage.cmake                     |   6 +-
 cmake/StrictAbi.cmake                         |   2 +-
 other/BUILD.bazel                             |   3 +
 other/DHT_bootstrap.c                         |   5 +-
 other/analysis/run-clang-tidy                 |   9 +
 other/analysis/run-cppcheck                   |   2 +
 other/bootstrap_daemon/BUILD.bazel            |   3 +
 other/bootstrap_daemon/docker/Dockerfile      |   2 +
 .../bootstrap_daemon/src/log_backend_stdout.h |   2 +-
 .../bootstrap_daemon/src/log_backend_syslog.h |   2 +-
 other/bootstrap_daemon/src/tox-bootstrapd.c   |   5 +-
 other/docker/modules/check                    |   1 +
 testing/BUILD.bazel                           |   3 +
 testing/Messenger_test.c                      |   5 +-
 testing/fuzzing/BUILD.bazel                   |   6 +
 testing/fuzzing/bootstrap_fuzz_test.cc        |   8 +-
 testing/fuzzing/e2e_fuzz_test.cc              |  15 +-
 testing/fuzzing/fuzz_support.cc               | 115 ++---
 testing/fuzzing/fuzz_support.hh               |  27 +-
 testing/fuzzing/protodump.cc                  |  14 +-
 testing/fuzzing/protodump_reduce.cc           |   8 +-
 testing/fuzzing/toxsave_fuzz_test.cc          |   9 +-
 tox.h                                         |   5 +
 toxav.h                                       |   5 +
 toxav/Makefile.inc                            |   5 +-
 toxav/groupav.c                               |   2 +-
 toxav/rtp_test.cc                             |   1 +
 toxav/toxav.c                                 |   4 +-
 toxav/toxav.h                                 |  13 +-
 toxav/toxav_old.c                             |   2 +-
 toxcore/BUILD.bazel                           | 299 ++++++++++-
 toxcore/DHT_test.cc                           |   3 +-
 toxcore/LAN_discovery.c                       |   1 +
 toxcore/Makefile.inc                          | 198 +++++---
 toxcore/Messenger.c                           |   2 +-
 toxcore/TCP_client.c                          |   1 +
 toxcore/TCP_common.c                          |   1 +
 toxcore/TCP_common.h                          |   1 +
 toxcore/TCP_connection.h                      |   2 +-
 toxcore/TCP_server.c                          |   7 +-
 toxcore/announce.c                            |  14 +-
 toxcore/bin_pack.c                            |   4 +
 toxcore/bin_pack.h                            |   1 +
 toxcore/bin_pack_test.cc                      |  29 +-
 toxcore/bin_unpack.c                          |  19 +-
 toxcore/bin_unpack.h                          |   3 +-
 toxcore/crypto_core.c                         |  39 +-
 toxcore/crypto_core.h                         |  44 +-
 toxcore/crypto_core_test_util.cc              |   9 +-
 toxcore/crypto_core_test_util.hh              |  15 +-
 toxcore/events/dht_get_nodes_response.c       |   1 +
 toxcore/events/friend_request.c               |   1 +
 toxcore/forwarding_fuzz_test.cc               |   2 +
 toxcore/friend_connection.c                   |   3 +-
 toxcore/group.c                               |   2 +-
 toxcore/group.h                               |   2 +-
 toxcore/group_announce.c                      |  85 ++--
 toxcore/group_announce_fuzz_test.cc           |  10 +-
 toxcore/group_announce_test.cc                |  16 +-
 toxcore/group_connection.c                    |  57 ++-
 toxcore/list_test.cc                          |  12 +-
 toxcore/logger.c                              |   3 +-
 toxcore/mem.c                                 |  59 +--
 toxcore/mem.h                                 |  21 +-
 toxcore/mem_test.cc                           |   8 +-
 toxcore/mem_test_util.cc                      |  24 +-
 toxcore/mem_test_util.hh                      |  20 +-
 toxcore/mono_time.c                           | 105 +---
 toxcore/mono_time.h                           |  12 +-
 toxcore/mono_time_test.cc                     |  18 +-
 toxcore/network.c                             | 479 +++++-------------
 toxcore/network.h                             |  66 +--
 toxcore/network_test_util.cc                  |  76 +--
 toxcore/network_test_util.hh                  |  42 +-
 toxcore/os_log.c                              |  31 ++
 toxcore/os_log.h                              |  22 +
 toxcore/os_memory.c                           |  43 ++
 toxcore/os_memory.h                           |  22 +
 toxcore/os_network.c                          | 409 +++++++++++++++
 toxcore/os_network.h                          |  22 +
 toxcore/os_network_impl.h                     |  39 ++
 toxcore/os_random.c                           |  45 ++
 toxcore/os_random.h                           |  26 +
 toxcore/os_system.c                           |  34 ++
 toxcore/os_system.h                           |  29 ++
 toxcore/os_time.c                             | 113 +++++
 toxcore/os_time.h                             |  22 +
 toxcore/ping_array_test.cc                    |  13 +-
 toxcore/sort_test_util.hh                     |   4 +-
 toxcore/tox.c                                 |  98 ++--
 toxcore/tox.h                                 | 439 +---------------
 toxcore/tox_api.c                             | 225 --------
 toxcore/tox_attributes.h                      |  36 ++
 toxcore/tox_event.h                           | 330 ++++++------
 toxcore/tox_events.c                          |   6 +-
 toxcore/tox_events.h                          |   2 -
 toxcore/tox_events_test.cc                    |  26 +-
 toxcore/{tox_struct.h => tox_impl.h}          |  13 +-
 toxcore/tox_log.c                             |  40 ++
 toxcore/tox_log.h                             |  42 ++
 toxcore/tox_log_impl.h                        |  54 ++
 toxcore/tox_log_level.c                       |  26 +
 toxcore/tox_log_level.h                       |  50 ++
 toxcore/tox_memory.c                          |  60 +++
 toxcore/tox_memory.h                          |  84 +++
 toxcore/tox_memory_impl.h                     |  49 ++
 toxcore/tox_network.c                         | 112 ++++
 toxcore/tox_network.h                         |  78 +++
 toxcore/tox_network_impl.h                    |  67 +++
 toxcore/tox_options.c                         | 216 ++++++++
 toxcore/tox_options.h                         | 479 ++++++++++++++++++
 toxcore/tox_private.c                         |  33 +-
 toxcore/tox_private.h                         |  28 +-
 toxcore/tox_random.c                          |  42 ++
 toxcore/tox_random.h                          |  37 ++
 toxcore/tox_random_impl.h                     |  53 ++
 toxcore/tox_system.c                          |  62 +++
 toxcore/tox_system.h                          |  53 ++
 toxcore/tox_system_impl.h                     |  31 ++
 toxcore/tox_test.cc                           |   2 +
 toxcore/tox_time.c                            |  37 ++
 toxcore/tox_time.h                            |  36 ++
 toxcore/tox_time_impl.h                       |  32 ++
 toxencryptsave.h                              |   5 +
 toxencryptsave/BUILD.bazel                    |   4 +
 toxencryptsave/Makefile.inc                   |   2 +-
 toxencryptsave/toxencryptsave.c               |  17 +-
 toxencryptsave/toxencryptsave.h               |   3 -
 153 files changed, 4183 insertions(+), 2099 deletions(-)
 create mode 100644 tox.h
 create mode 100644 toxav.h
 create mode 100644 toxcore/os_log.c
 create mode 100644 toxcore/os_log.h
 create mode 100644 toxcore/os_memory.c
 create mode 100644 toxcore/os_memory.h
 create mode 100644 toxcore/os_network.c
 create mode 100644 toxcore/os_network.h
 create mode 100644 toxcore/os_network_impl.h
 create mode 100644 toxcore/os_random.c
 create mode 100644 toxcore/os_random.h
 create mode 100644 toxcore/os_system.c
 create mode 100644 toxcore/os_system.h
 create mode 100644 toxcore/os_time.c
 create mode 100644 toxcore/os_time.h
 create mode 100644 toxcore/tox_attributes.h
 rename toxcore/{tox_struct.h => tox_impl.h} (93%)
 create mode 100644 toxcore/tox_log.c
 create mode 100644 toxcore/tox_log.h
 create mode 100644 toxcore/tox_log_impl.h
 create mode 100644 toxcore/tox_log_level.c
 create mode 100644 toxcore/tox_log_level.h
 create mode 100644 toxcore/tox_memory.c
 create mode 100644 toxcore/tox_memory.h
 create mode 100644 toxcore/tox_memory_impl.h
 create mode 100644 toxcore/tox_network.c
 create mode 100644 toxcore/tox_network.h
 create mode 100644 toxcore/tox_network_impl.h
 create mode 100644 toxcore/tox_options.c
 create mode 100644 toxcore/tox_options.h
 create mode 100644 toxcore/tox_random.c
 create mode 100644 toxcore/tox_random.h
 create mode 100644 toxcore/tox_random_impl.h
 create mode 100644 toxcore/tox_system.c
 create mode 100644 toxcore/tox_system.h
 create mode 100644 toxcore/tox_system_impl.h
 create mode 100644 toxcore/tox_time.c
 create mode 100644 toxcore/tox_time.h
 create mode 100644 toxcore/tox_time_impl.h
 create mode 100644 toxencryptsave.h

diff --git a/BUILD.bazel b/BUILD.bazel
index 481b46ae39e..91fcefa33ba 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -3,38 +3,76 @@ load("//tools/project:build_defs.bzl", "project")
 
 project(license = "gpl3-https")
 
+genrule(
+    name = "toxcore_headers",
+    srcs = ["//c-toxcore/toxcore:public"],
+    outs = [
+        "tox/toxcore/os_log.h",
+        "tox/toxcore/os_memory.h",
+        "tox/toxcore/os_network.h",
+        "tox/toxcore/os_random.h",
+        "tox/toxcore/os_system.h",
+        "tox/toxcore/tox.h",
+        "tox/toxcore/tox_attributes.h",
+        "tox/toxcore/tox_dispatch.h",
+        "tox/toxcore/tox_events.h",
+        "tox/toxcore/tox_log.h",
+        "tox/toxcore/tox_log_impl.h",
+        "tox/toxcore/tox_log_level.h",
+        "tox/toxcore/tox_memory.h",
+        "tox/toxcore/tox_memory_impl.h",
+        "tox/toxcore/tox_network.h",
+        "tox/toxcore/tox_network_impl.h",
+        "tox/toxcore/tox_options.h",
+        "tox/toxcore/tox_random.h",
+        "tox/toxcore/tox_random_impl.h",
+        "tox/toxcore/tox_system.h",
+        "tox/toxcore/tox_system_impl.h",
+        "tox/toxcore/tox_time.h",
+        "tox/toxcore/tox_time_impl.h",
+    ],
+    cmd = "cp $(locations //c-toxcore/toxcore:public) $(GENDIR)/c-toxcore/tox/toxcore/",
+    visibility = ["//visibility:public"],
+)
+
 genrule(
     name = "public_headers",
     srcs = [
+        "tox.h",
+        "toxav.h",
+        "toxencryptsave.h",
         "//c-toxcore/toxav:toxav.h",
-        "//c-toxcore/toxcore:tox.h",
-        "//c-toxcore/toxcore:tox_dispatch.h",
-        "//c-toxcore/toxcore:tox_events.h",
-        "//c-toxcore/toxcore:tox_private.h",
         "//c-toxcore/toxencryptsave:toxencryptsave.h",
     ],
     outs = [
-        "tox/toxav.h",
         "tox/tox.h",
-        "tox/tox_dispatch.h",
-        "tox/tox_events.h",
-        "tox/tox_private.h",
+        "tox/toxav.h",
+        "tox/toxav/toxav.h",
         "tox/toxencryptsave.h",
+        "tox/toxencryptsave/toxencryptsave.h",
     ],
     cmd = """
-        cp $(location //c-toxcore/toxav:toxav.h) $(GENDIR)/c-toxcore/tox/toxav.h
-        cp $(location //c-toxcore/toxcore:tox.h) $(GENDIR)/c-toxcore/tox/tox.h
-        cp $(location //c-toxcore/toxcore:tox_dispatch.h) $(GENDIR)/c-toxcore/tox/tox_dispatch.h
-        cp $(location //c-toxcore/toxcore:tox_events.h) $(GENDIR)/c-toxcore/tox/tox_events.h
-        cp $(location //c-toxcore/toxcore:tox_private.h) $(GENDIR)/c-toxcore/tox/tox_private.h
-        cp $(location //c-toxcore/toxencryptsave:toxencryptsave.h) $(GENDIR)/c-toxcore/tox/toxencryptsave.h
+        cp $(location toxav.h) $(GENDIR)/c-toxcore/tox/toxav.h
+        cp $(location tox.h) $(GENDIR)/c-toxcore/tox/tox.h
+        cp $(location toxencryptsave.h) $(GENDIR)/c-toxcore/tox/toxencryptsave.h
+        cp $(location //c-toxcore/toxav:toxav.h) $(GENDIR)/c-toxcore/tox/toxav/toxav.h
+        cp $(location //c-toxcore/toxencryptsave:toxencryptsave.h) $(GENDIR)/c-toxcore/tox/toxencryptsave/toxencryptsave.h
     """,
     visibility = ["//visibility:public"],
 )
 
+filegroup(
+    name = "public",
+    srcs = [
+        ":public_headers",
+        ":toxcore_headers",
+    ],
+    visibility = ["//visibility:public"],
+)
+
 cc_library(
     name = "c-toxcore",
-    hdrs = [":public_headers"],
+    hdrs = [":public"],
     includes = ["."],
     visibility = ["//visibility:public"],
     deps = [
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9237d21c117..f8b885985e3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,6 +30,7 @@ endif()
 set_source_files_properties(
   toxcore/mono_time.c
   toxcore/network.c
+  toxcore/os_time.c
   toxcore/tox.c
   toxcore/util.c
   PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE)
@@ -319,6 +320,19 @@ set(toxcore_SOURCES
   toxcore/onion_client.c
   toxcore/onion_client.h
   toxcore/onion.h
+  toxcore/os_log.c
+  toxcore/os_log.h
+  toxcore/os_memory.c
+  toxcore/os_memory.h
+  toxcore/os_network.c
+  toxcore/os_network.h
+  toxcore/os_network_impl.h
+  toxcore/os_random.c
+  toxcore/os_random.h
+  toxcore/os_system.c
+  toxcore/os_system.h
+  toxcore/os_time.c
+  toxcore/os_time.h
   toxcore/ping_array.c
   toxcore/ping_array.h
   toxcore/ping.c
@@ -341,13 +355,35 @@ set(toxcore_SOURCES
   toxcore/timed_auth.h
   toxcore/tox_api.c
   toxcore/tox.c
+  toxcore/tox.h
   toxcore/tox_dispatch.c
   toxcore/tox_dispatch.h
   toxcore/tox_event.c
   toxcore/tox_event.h
   toxcore/tox_events.c
   toxcore/tox_events.h
-  toxcore/tox.h
+  toxcore/tox_log.c
+  toxcore/tox_log.h
+  toxcore/tox_log_impl.h
+  toxcore/tox_log_level.c
+  toxcore/tox_log_level.h
+  toxcore/tox_memory.c
+  toxcore/tox_memory.h
+  toxcore/tox_memory_impl.h
+  toxcore/tox_network.c
+  toxcore/tox_network.h
+  toxcore/tox_network_impl.h
+  toxcore/tox_options.c
+  toxcore/tox_options.h
+  toxcore/tox_random.c
+  toxcore/tox_random.h
+  toxcore/tox_random_impl.h
+  toxcore/tox_system.c
+  toxcore/tox_system.h
+  toxcore/tox_system_impl.h
+  toxcore/tox_time.c
+  toxcore/tox_time.h
+  toxcore/tox_time_impl.h
   toxcore/tox_private.c
   toxcore/tox_private.h
   toxcore/tox_pack.c
@@ -368,13 +404,25 @@ else()
 endif()
 set(toxcore_PKGCONFIG_REQUIRES ${toxcore_PKGCONFIG_REQUIRES} libsodium)
 set(toxcore_API_HEADERS
+  ${toxcore_SOURCE_DIR}/tox.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/os_log.h^os
+  ${toxcore_SOURCE_DIR}/toxcore/os_memory.h^os
+  ${toxcore_SOURCE_DIR}/toxcore/os_network.h^os
+  ${toxcore_SOURCE_DIR}/toxcore/os_random.h^os
+  ${toxcore_SOURCE_DIR}/toxcore/os_system.h^os
   ${toxcore_SOURCE_DIR}/toxcore/tox.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_attributes.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_dispatch.h^tox
   ${toxcore_SOURCE_DIR}/toxcore/tox_events.h^tox
-  ${toxcore_SOURCE_DIR}/toxcore/tox_dispatch.h^tox)
-if(EXPERIMENTAL_API)
-  set(toxcore_API_HEADERS ${toxcore_API_HEADERS}
-    ${toxcore_SOURCE_DIR}/toxcore/tox_private.h^tox)
-endif()
+  ${toxcore_SOURCE_DIR}/toxcore/tox_log.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_log_level.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_memory.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_network.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_options.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_random.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_system.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_system_impl.h^tox
+  ${toxcore_SOURCE_DIR}/toxcore/tox_time.h^tox)
 
 ################################################################################
 #
@@ -403,6 +451,7 @@ if(BUILD_TOXAV)
     toxav/video.c
     toxav/video.h)
   set(toxcore_API_HEADERS ${toxcore_API_HEADERS}
+    ${toxcore_SOURCE_DIR}/toxav.h^toxav
     ${toxcore_SOURCE_DIR}/toxav/toxav.h^toxav)
 
   if(TARGET Opus::opus AND TARGET libvpx::libvpx)
@@ -428,6 +477,7 @@ set(toxcore_SOURCES ${toxcore_SOURCES}
   toxencryptsave/toxencryptsave.c
   toxencryptsave/toxencryptsave.h)
 set(toxcore_API_HEADERS ${toxcore_API_HEADERS}
+  ${toxcore_SOURCE_DIR}/toxencryptsave.h^tox
   ${toxcore_SOURCE_DIR}/toxencryptsave/toxencryptsave.h^tox)
 
 ################################################################################
diff --git a/auto_tests/BUILD.bazel b/auto_tests/BUILD.bazel
index 24a3addbe03..a41e02cd96a 100644
--- a/auto_tests/BUILD.bazel
+++ b/auto_tests/BUILD.bazel
@@ -19,6 +19,7 @@ cc_library(
         "//c-toxcore/toxcore:tox",
         "//c-toxcore/toxcore:tox_dispatch",
         "//c-toxcore/toxcore:tox_events",
+        "//c-toxcore/toxcore:tox_time",
     ],
 )
 
@@ -75,10 +76,14 @@ extra_data = {
         "//c-toxcore/toxcore:onion",
         "//c-toxcore/toxcore:onion_announce",
         "//c-toxcore/toxcore:onion_client",
+        "//c-toxcore/toxcore:os_memory",
+        "//c-toxcore/toxcore:os_network",
+        "//c-toxcore/toxcore:os_random",
         "//c-toxcore/toxcore:tox",
         "//c-toxcore/toxcore:tox_dispatch",
         "//c-toxcore/toxcore:tox_events",
         "//c-toxcore/toxcore:tox_unpack",
+        "//c-toxcore/toxcore:tox_time",
         "//c-toxcore/toxcore:util",
         "//c-toxcore/toxencryptsave",
         "@libsodium",
diff --git a/auto_tests/TCP_test.c b/auto_tests/TCP_test.c
index 1cf7748efa7..95bf00dd960 100644
--- a/auto_tests/TCP_test.c
+++ b/auto_tests/TCP_test.c
@@ -8,7 +8,9 @@
 #include "../toxcore/TCP_server.h"
 #include "../toxcore/crypto_core.h"
 #include "../toxcore/mono_time.h"
-#include "../toxcore/network.h"
+#include "../toxcore/os_random.h"
+#include "../toxcore/os_network.h"
+#include "../toxcore/os_memory.h"
 #include "auto_test_support.h"
 
 #define NUM_PORTS 3
@@ -52,7 +54,7 @@ static void test_basic(void)
     const Memory *mem = os_memory();
     ck_assert(mem != nullptr);
 
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     Logger *logger = logger_new(mem);
     logger_callback_log(logger, print_debug_logger, nullptr, nullptr);
 
@@ -319,7 +321,7 @@ static void test_some(void)
     const Memory *mem = os_memory();
     ck_assert(mem != nullptr);
 
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     Logger *logger = logger_new(mem);
 
     uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
@@ -518,7 +520,7 @@ static void test_client(void)
     ck_assert(mem != nullptr);
 
     Logger *logger = logger_new(mem);
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
 
     uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
     uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE];
@@ -655,7 +657,7 @@ static void test_client_invalid(void)
     const Memory *mem = os_memory();
     ck_assert(mem != nullptr);
 
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     Logger *logger = logger_new(mem);
 
     uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE];
@@ -734,7 +736,7 @@ static void test_tcp_connection(void)
     const Memory *mem = os_memory();
     ck_assert(mem != nullptr);
 
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     Logger *logger = logger_new(mem);
 
     tcp_data_callback_called = 0;
@@ -849,7 +851,7 @@ static void test_tcp_connection2(void)
     const Memory *mem = os_memory();
     ck_assert(mem != nullptr);
 
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     Logger *logger = logger_new(mem);
 
     tcp_oobdata_callback_called = 0;
diff --git a/auto_tests/announce_test.c b/auto_tests/announce_test.c
index 7e387c27398..dfb950d7266 100644
--- a/auto_tests/announce_test.c
+++ b/auto_tests/announce_test.c
@@ -7,6 +7,9 @@
 #include "../toxcore/mono_time.h"
 #include "../toxcore/forwarding.h"
 #include "../toxcore/net_crypto.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_network.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/util.h"
 #include "auto_test_support.h"
 #include "check_compat.h"
@@ -60,7 +63,7 @@ static void test_store_data(void)
     Logger *log = logger_new(mem);
     ck_assert(log != nullptr);
     logger_callback_log(log, print_debug_logger, nullptr, nullptr);
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     ck_assert(mono_time != nullptr);
     Networking_Core *net = new_networking_no_udp(log, mem, ns);
     ck_assert(net != nullptr);
diff --git a/auto_tests/auto_test_support.c b/auto_tests/auto_test_support.c
index 483e3130b7a..c71a3f032d9 100644
--- a/auto_tests/auto_test_support.c
+++ b/auto_tests/auto_test_support.c
@@ -7,7 +7,8 @@
 #include "../toxcore/mono_time.h"
 #include "../toxcore/tox_dispatch.h"
 #include "../toxcore/tox_events.h"
-#include "../toxcore/tox_struct.h"
+#include "../toxcore/tox_impl.h"
+#include "../toxcore/tox_time_impl.h"
 
 #include "auto_test_support.h"
 
@@ -157,17 +158,25 @@ static uint64_t get_state_clock_callback(void *user_data)
     return *clock;
 }
 
+static const Tox_Time_Funcs autotox_time_funcs = {
+    get_state_clock_callback,
+};
+
 void set_mono_time_callback(AutoTox *autotox)
 {
     ck_assert(autotox != nullptr);
 
+    if (autotox->tm == nullptr) {
+        autotox->tm = tox_time_new(&autotox_time_funcs, &autotox->clock, autotox->tox->sys.mem);
+    }
+
     Mono_Time *mono_time = autotox->tox->mono_time;
 
+    mono_time_set_current_time_callback(mono_time, nullptr);  // set to default first
     autotox->clock = current_time_monotonic(mono_time);
     ck_assert_msg(autotox->clock >= 1000,
                   "clock is too low (not initialised?): %lu", (unsigned long)autotox->clock);
-    mono_time_set_current_time_callback(mono_time, nullptr, nullptr);  // set to default first
-    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &autotox->clock);
+    mono_time_set_current_time_callback(mono_time, autotox->tm);
 }
 
 void save_autotox(AutoTox *autotox)
@@ -194,6 +203,8 @@ void kill_autotox(AutoTox *autotox)
     autotox->alive = false;
     tox_dispatch_free(autotox->dispatch);
     tox_kill(autotox->tox);
+    tox_time_free(autotox->tm);
+    autotox->tm = nullptr;
 }
 
 void reload(AutoTox *autotox)
@@ -399,6 +410,7 @@ void run_auto_test(struct Tox_Options *options, uint32_t tox_count, autotox_test
     for (uint32_t i = 0; i < tox_count; ++i) {
         tox_dispatch_free(autotoxes[i].dispatch);
         tox_kill(autotoxes[i].tox);
+        tox_time_free(autotoxes[i].tm);
         free(autotoxes[i].state);
         free(autotoxes[i].save_state);
     }
diff --git a/auto_tests/auto_test_support.h b/auto_tests/auto_test_support.h
index cd883e28c94..210feae0a1a 100644
--- a/auto_tests/auto_test_support.h
+++ b/auto_tests/auto_test_support.h
@@ -12,6 +12,7 @@
 typedef struct AutoTox {
     Tox *tox;
     Tox_Dispatch *dispatch;
+    Tox_Time *tm;
 
     uint32_t index;
     uint64_t clock;
diff --git a/auto_tests/conference_av_test.c b/auto_tests/conference_av_test.c
index 28a2a3a1e37..c070e9ed115 100644
--- a/auto_tests/conference_av_test.c
+++ b/auto_tests/conference_av_test.c
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include "../toxav/toxav.h"
+#include "../toxcore/os_random.h"
 #include "check_compat.h"
 
 #define NUM_AV_GROUP_TOX 16
diff --git a/auto_tests/conference_test.c b/auto_tests/conference_test.c
index af1cd288f83..8426cccf306 100644
--- a/auto_tests/conference_test.c
+++ b/auto_tests/conference_test.c
@@ -6,6 +6,7 @@
 #include <time.h>
 #include <stdint.h>
 
+#include "../toxcore/os_random.h"
 #include "../toxcore/util.h"
 
 #include "check_compat.h"
diff --git a/auto_tests/crypto_test.c b/auto_tests/crypto_test.c
index 75d94ea1b24..7fe90b1e23f 100644
--- a/auto_tests/crypto_test.c
+++ b/auto_tests/crypto_test.c
@@ -4,6 +4,8 @@
 
 #include "../testing/misc_tools.h"
 #include "../toxcore/crypto_core.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/net_crypto.h"
 #include "check_compat.h"
 
@@ -250,6 +252,7 @@ static void test_large_data_symmetric(void)
     ck_assert(mem != nullptr);
     const Random *rng = os_random();
     ck_assert(rng != nullptr);
+
     uint8_t k[CRYPTO_SYMMETRIC_KEY_SIZE];
 
     uint8_t n[CRYPTO_NONCE_SIZE];
diff --git a/auto_tests/encryptsave_test.c b/auto_tests/encryptsave_test.c
index 3573337e492..be55cfcbebf 100644
--- a/auto_tests/encryptsave_test.c
+++ b/auto_tests/encryptsave_test.c
@@ -6,6 +6,7 @@
 #include "../testing/misc_tools.h"
 #include "../toxcore/ccompat.h"
 #include "../toxcore/crypto_core.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/tox.h"
 #include "../toxencryptsave/toxencryptsave.h"
 #include "auto_test_support.h"
diff --git a/auto_tests/forwarding_test.c b/auto_tests/forwarding_test.c
index a1b8d6d2486..97f4949de87 100644
--- a/auto_tests/forwarding_test.c
+++ b/auto_tests/forwarding_test.c
@@ -8,6 +8,9 @@
 #include "../toxcore/mono_time.h"
 #include "../toxcore/forwarding.h"
 #include "../toxcore/net_crypto.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_network.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/util.h"
 #include "auto_test_support.h"
 #include "check_compat.h"
@@ -115,7 +118,7 @@ static Forwarding_Subtox *new_forwarding_subtox(const Memory *mem, bool no_udp,
     subtox->log = logger_new(mem);
     ck_assert(subtox->log != nullptr);
     logger_callback_log(subtox->log, print_debug_logger, nullptr, index);
-    subtox->mono_time = mono_time_new(mem, nullptr, nullptr);
+    subtox->mono_time = mono_time_new(mem, nullptr);
 
     if (no_udp) {
         subtox->net = new_networking_no_udp(subtox->log, mem, ns);
diff --git a/auto_tests/group_message_test.c b/auto_tests/group_message_test.c
index 380a994b565..7ebe8cba6c8 100644
--- a/auto_tests/group_message_test.c
+++ b/auto_tests/group_message_test.c
@@ -12,6 +12,7 @@
 
 #include "auto_test_support.h"
 #include "check_compat.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/util.h"
 
 typedef struct State {
diff --git a/auto_tests/group_sync_test.c b/auto_tests/group_sync_test.c
index 146c2258dda..f8d2997f9be 100644
--- a/auto_tests/group_sync_test.c
+++ b/auto_tests/group_sync_test.c
@@ -10,6 +10,7 @@
 
 #include "auto_test_support.h"
 
+#include "../toxcore/os_random.h"
 #include "../toxcore/tox.h"
 #include "../toxcore/util.h"
 
diff --git a/auto_tests/group_topic_test.c b/auto_tests/group_topic_test.c
index c8d84cb7e01..81f18fc9e1c 100644
--- a/auto_tests/group_topic_test.c
+++ b/auto_tests/group_topic_test.c
@@ -11,8 +11,9 @@
 #include "auto_test_support.h"
 #include "check_compat.h"
 
-#include "../toxcore/tox.h"
 #include "../toxcore/group_chats.h"
+#include "../toxcore/os_random.h"
+#include "../toxcore/tox.h"
 
 #define NUM_GROUP_TOXES 3
 
diff --git a/auto_tests/lan_discovery_test.c b/auto_tests/lan_discovery_test.c
index d7bf594b2bf..847d883dbb1 100644
--- a/auto_tests/lan_discovery_test.c
+++ b/auto_tests/lan_discovery_test.c
@@ -3,7 +3,9 @@
 
 #include "../testing/misc_tools.h"
 #include "../toxcore/ccompat.h"
-#include "../toxcore/tox_struct.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/tox_time_impl.h"
+#include "../toxcore/tox_impl.h"
 #include "auto_test_support.h"
 
 static uint64_t get_state_clock_callback(void *user_data)
@@ -12,6 +14,10 @@ static uint64_t get_state_clock_callback(void *user_data)
     return *clock;
 }
 
+static const Tox_Time_Funcs mock_time_funcs = {
+    get_state_clock_callback,
+};
+
 int main(void)
 {
     setvbuf(stdout, nullptr, _IONBF, 0);
@@ -21,14 +27,18 @@ int main(void)
     ck_assert(tox1 != nullptr);
     ck_assert(tox2 != nullptr);
 
+    const Memory *mem = os_memory();
+
     uint64_t clock = current_time_monotonic(tox1->mono_time);
+    Tox_Time *tm = tox_time_new(&mock_time_funcs, &clock, mem);
+
     Mono_Time *mono_time;
 
     mono_time = tox1->mono_time;
-    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
+    mono_time_set_current_time_callback(mono_time, tm);
 
     mono_time = tox2->mono_time;
-    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
+    mono_time_set_current_time_callback(mono_time, tm);
 
     printf("Waiting for LAN discovery. This loop will attempt to run until successful.");
 
@@ -49,5 +59,7 @@ int main(void)
 
     tox_kill(tox2);
     tox_kill(tox1);
+
+    tox_time_free(tm);
     return 0;
 }
diff --git a/auto_tests/network_test.c b/auto_tests/network_test.c
index a2bc301a2f8..49b61a62b6c 100644
--- a/auto_tests/network_test.c
+++ b/auto_tests/network_test.c
@@ -3,6 +3,8 @@
 
 #include "../testing/misc_tools.h"
 #include "../toxcore/network.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_network.h"
 #include "check_compat.h"
 
 #ifndef USE_IPV6
diff --git a/auto_tests/onion_test.c b/auto_tests/onion_test.c
index b73ffbd1655..aacbb35b652 100644
--- a/auto_tests/onion_test.c
+++ b/auto_tests/onion_test.c
@@ -6,6 +6,9 @@
 #include "../toxcore/onion.h"
 #include "../toxcore/onion_announce.h"
 #include "../toxcore/onion_client.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_network.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/util.h"
 #include "auto_test_support.h"
 #include "check_compat.h"
@@ -233,8 +236,8 @@ static void test_basic(void)
     Logger *log2 = logger_new(mem);
     logger_callback_log(log2, print_debug_logger, nullptr, &index[1]);
 
-    Mono_Time *mono_time1 = mono_time_new(mem, nullptr, nullptr);
-    Mono_Time *mono_time2 = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time1 = mono_time_new(mem, nullptr);
+    Mono_Time *mono_time2 = mono_time_new(mem, nullptr);
 
     IP ip = get_loopback();
     Onion *onion1 = new_onion(log1, mem, mono_time1, rng, new_dht(log1, mem, rng, ns, mono_time1, new_networking(log1, mem, ns, &ip, 36567), true, false));
@@ -332,7 +335,7 @@ static void test_basic(void)
     Logger *log3 = logger_new(mem);
     logger_callback_log(log3, print_debug_logger, nullptr, &index[2]);
 
-    Mono_Time *mono_time3 = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time3 = mono_time_new(mem, nullptr);
 
     Onion *onion3 = new_onion(log3, mem, mono_time3, rng, new_dht(log3, mem, rng, ns, mono_time3, new_networking(log3, mem, ns, &ip, 36569), true, false));
     ck_assert_msg((onion3 != nullptr), "Onion failed initializing.");
@@ -421,7 +424,7 @@ static Onions *new_onions(const Memory *mem, const Random *rng, uint16_t port, u
 
     logger_callback_log(on->log, print_debug_logger, nullptr, index);
 
-    on->mono_time = mono_time_new(mem, nullptr, nullptr);
+    on->mono_time = mono_time_new(mem, nullptr);
 
     if (!on->mono_time) {
         logger_kill(on->log);
diff --git a/auto_tests/reconnect_test.c b/auto_tests/reconnect_test.c
index 8a4b31c4953..ffb99799f94 100644
--- a/auto_tests/reconnect_test.c
+++ b/auto_tests/reconnect_test.c
@@ -11,6 +11,7 @@
 
 #include "../testing/misc_tools.h"
 #include "../toxcore/friend_connection.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/tox.h"
 #include "../toxcore/util.h"
 #include "check_compat.h"
diff --git a/auto_tests/save_friend_test.c b/auto_tests/save_friend_test.c
index 339690f2b8a..5b82c63470d 100644
--- a/auto_tests/save_friend_test.c
+++ b/auto_tests/save_friend_test.c
@@ -8,6 +8,7 @@
 #include "../testing/misc_tools.h"
 #include "../toxcore/ccompat.h"
 #include "../toxcore/crypto_core.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/tox.h"
 #include "auto_test_support.h"
 #include "check_compat.h"
diff --git a/auto_tests/save_load_test.c b/auto_tests/save_load_test.c
index f4158856de8..cad6caafde0 100644
--- a/auto_tests/save_load_test.c
+++ b/auto_tests/save_load_test.c
@@ -8,8 +8,10 @@
 
 #include "../testing/misc_tools.h"
 #include "../toxcore/ccompat.h"
+#include "../toxcore/os_memory.h"
 #include "../toxcore/tox.h"
-#include "../toxcore/tox_struct.h"
+#include "../toxcore/tox_impl.h"
+#include "../toxcore/tox_time_impl.h"
 #include "../toxcore/util.h"
 #include "auto_test_support.h"
 #include "check_compat.h"
@@ -130,6 +132,10 @@ static uint64_t get_state_clock_callback(void *user_data)
     return clock;
 }
 
+static const Tox_Time_Funcs mock_time_funcs = {
+    get_state_clock_callback,
+};
+
 static void increment_clock(Time_Data *time_data, uint64_t count)
 {
     pthread_mutex_lock(&time_data->lock);
@@ -137,10 +143,10 @@ static void increment_clock(Time_Data *time_data, uint64_t count)
     pthread_mutex_unlock(&time_data->lock);
 }
 
-static void set_current_time_callback(Tox *tox, Time_Data *time_data)
+static void set_current_time_callback(Tox *tox, Tox_Time *tm)
 {
     Mono_Time *mono_time = tox->mono_time;
-    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, time_data);
+    mono_time_set_current_time_callback(mono_time, tm);
 }
 
 static void test_few_clients(void)
@@ -180,9 +186,12 @@ static void test_few_clients(void)
     Time_Data time_data;
     ck_assert_msg(pthread_mutex_init(&time_data.lock, nullptr) == 0, "Failed to init time_data mutex");
     time_data.clock = current_time_monotonic(tox1->mono_time);
-    set_current_time_callback(tox1, &time_data);
-    set_current_time_callback(tox2, &time_data);
-    set_current_time_callback(tox3, &time_data);
+
+    const Memory *mem = os_memory();
+    Tox_Time *tm = tox_time_new(&mock_time_funcs, &time_data, mem);
+    set_current_time_callback(tox1, tm);
+    set_current_time_callback(tox2, tm);
+    set_current_time_callback(tox3, tm);
 
     uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
     tox_self_get_dht_id(tox1, dht_key);
@@ -304,6 +313,8 @@ static void test_few_clients(void)
 
     tox_options_free(opts2);
     tox_options_free(opts3);
+
+    tox_time_free(tm);
 }
 
 int main(void)
diff --git a/auto_tests/tox_events_test.c b/auto_tests/tox_events_test.c
index 1b7cb729aa0..bd7e0f24c05 100644
--- a/auto_tests/tox_events_test.c
+++ b/auto_tests/tox_events_test.c
@@ -7,9 +7,11 @@
 #include <time.h>
 
 #include "../testing/misc_tools.h"
+#include "../toxcore/os_memory.h"
 #include "../toxcore/tox.h"
 #include "../toxcore/tox_events.h"
-#include "../toxcore/tox_struct.h"
+#include "../toxcore/tox_impl.h"
+#include "../toxcore/tox_time_impl.h"
 #include "auto_test_support.h"
 #include "check_compat.h"
 
@@ -54,6 +56,9 @@ static uint64_t get_state_clock_callback(void *user_data)
     const uint64_t *clock = (const uint64_t *)user_data;
     return *clock;
 }
+static const Tox_Time_Funcs mock_time_funcs = {
+    get_state_clock_callback,
+};
 
 static void test_tox_events(void)
 {
@@ -70,13 +75,17 @@ static void test_tox_events(void)
         ck_assert_msg(toxes[i] != nullptr, "failed to create tox instances %u", i);
     }
 
+    const Memory *mem = os_memory();
+
     uint64_t clock = current_time_monotonic(toxes[0]->mono_time);
+    Tox_Time *tm = tox_time_new(&mock_time_funcs, &clock, mem);
+
     Mono_Time *mono_time;
 
     mono_time = toxes[0]->mono_time;
-    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
+    mono_time_set_current_time_callback(mono_time, tm);
     mono_time = toxes[1]->mono_time;
-    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, &clock);
+    mono_time_set_current_time_callback(mono_time, tm);
 
     uint8_t pk[TOX_PUBLIC_KEY_SIZE];
     tox_self_get_dht_id(toxes[0], pk);
@@ -124,6 +133,8 @@ static void test_tox_events(void)
     for (uint32_t i = 0; i < 2; ++i) {
         tox_kill(toxes[i]);
     }
+
+    tox_time_free(tm);
 }
 
 int main(void)
diff --git a/auto_tests/tox_many_tcp_test.c b/auto_tests/tox_many_tcp_test.c
index 0f883005624..fcb035ca995 100644
--- a/auto_tests/tox_many_tcp_test.c
+++ b/auto_tests/tox_many_tcp_test.c
@@ -8,6 +8,7 @@
 
 #include "../testing/misc_tools.h"
 #include "../toxcore/crypto_core.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/tox.h"
 #include "../toxcore/util.h"
 #include "auto_test_support.h"
diff --git a/auto_tests/tox_many_test.c b/auto_tests/tox_many_test.c
index 7a53191d404..56f839b33b4 100644
--- a/auto_tests/tox_many_test.c
+++ b/auto_tests/tox_many_test.c
@@ -8,6 +8,7 @@
 
 #include "../testing/misc_tools.h"
 #include "../toxcore/crypto_core.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/tox.h"
 #include "../toxcore/util.h"
 #include "auto_test_support.h"
diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c
index fe1a1033c66..801d9929c32 100644
--- a/auto_tests/toxav_many_test.c
+++ b/auto_tests/toxav_many_test.c
@@ -15,8 +15,10 @@
 #include "../toxav/toxav.h"
 #include "../toxcore/crypto_core.h"
 #include "../toxcore/logger.h"
+#include "../toxcore/os_memory.h"
 #include "../toxcore/tox.h"
-#include "../toxcore/tox_struct.h"
+#include "../toxcore/tox_impl.h"
+#include "../toxcore/tox_time_impl.h"
 #include "../toxcore/util.h"
 #include "auto_test_support.h"
 #include "check_compat.h"
@@ -143,6 +145,10 @@ static uint64_t get_state_clock_callback(void *user_data)
     return clock;
 }
 
+static const Tox_Time_Funcs mock_time_funcs = {
+    get_state_clock_callback,
+};
+
 static void increment_clock(Time_Data *time_data, uint64_t count)
 {
     pthread_mutex_lock(&time_data->lock);
@@ -150,10 +156,10 @@ static void increment_clock(Time_Data *time_data, uint64_t count)
     pthread_mutex_unlock(&time_data->lock);
 }
 
-static void set_current_time_callback(Tox *tox, Time_Data *time_data)
+static void set_current_time_callback(Tox *tox, Tox_Time *tm)
 {
     Mono_Time *mono_time = tox->mono_time;
-    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, time_data);
+    mono_time_set_current_time_callback(mono_time, tm);
 }
 
 static void test_av_three_calls(void)
@@ -167,6 +173,10 @@ static void test_av_three_calls(void)
 
     Time_Data time_data;
     pthread_mutex_init(&time_data.lock, nullptr);
+
+    const Memory *mem = os_memory();
+    Tox_Time *tm = tox_time_new(&mock_time_funcs, &time_data, mem);
+
     {
         Tox_Options *opts = tox_options_new(nullptr);
         ck_assert(opts != nullptr);
@@ -177,23 +187,23 @@ static void test_av_three_calls(void)
         ck_assert(error == TOX_ERR_NEW_OK);
 
         time_data.clock = current_time_monotonic(bootstrap->mono_time);
-        set_current_time_callback(bootstrap, &time_data);
+        set_current_time_callback(bootstrap, tm);
 
         alice = tox_new_log(opts, &error, &index[1]);
         ck_assert(error == TOX_ERR_NEW_OK);
-        set_current_time_callback(alice, &time_data);
+        set_current_time_callback(alice, tm);
 
         bobs[0] = tox_new_log(opts, &error, &index[2]);
         ck_assert(error == TOX_ERR_NEW_OK);
-        set_current_time_callback(bobs[0], &time_data);
+        set_current_time_callback(bobs[0], tm);
 
         bobs[1] = tox_new_log(opts, &error, &index[3]);
         ck_assert(error == TOX_ERR_NEW_OK);
-        set_current_time_callback(bobs[1], &time_data);
+        set_current_time_callback(bobs[1], tm);
 
         bobs[2] = tox_new_log(opts, &error, &index[4]);
         ck_assert(error == TOX_ERR_NEW_OK);
-        set_current_time_callback(bobs[2], &time_data);
+        set_current_time_callback(bobs[2], tm);
         tox_options_free(opts);
     }
 
@@ -369,6 +379,8 @@ static void test_av_three_calls(void)
     tox_kill(alice);
     tox_kill(bootstrap);
 
+    tox_time_free(tm);
+
     pthread_mutex_destroy(&time_data.lock);
 
     printf("\nTest successful!\n");
diff --git a/cmake/ModulePackage.cmake b/cmake/ModulePackage.cmake
index c20a80a1f78..35ca8b91272 100644
--- a/cmake/ModulePackage.cmake
+++ b/cmake/ModulePackage.cmake
@@ -20,6 +20,7 @@ if(FULLY_STATIC)
 endif()
 
 function(install_module lib)
+  cmake_parse_arguments(INSTALL_MODULE "" "DESTINATION" "" ${ARGN})
   if(TARGET ${lib}_shared)
     set_target_properties(${lib}_shared PROPERTIES
       VERSION ${SOVERSION}
@@ -59,7 +60,10 @@ function(install_module lib)
   foreach(sublib ${${lib}_API_HEADERS})
     string(REPLACE "^" ";" sublib ${sublib})
     list(GET sublib 0 header)
+    string(REPLACE "${${lib}_SOURCE_DIR}/" "" target_header ${header})
+    get_filename_component(target_path ${target_header} DIRECTORY)
 
-    install(FILES ${header} ${ARGN})
+    install(FILES ${header} DESTINATION
+            "${INSTALL_MODULE_DESTINATION}/${target_path}")
   endforeach()
 endfunction()
diff --git a/cmake/StrictAbi.cmake b/cmake/StrictAbi.cmake
index ec5792199b3..75af030c89f 100644
--- a/cmake/StrictAbi.cmake
+++ b/cmake/StrictAbi.cmake
@@ -32,7 +32,7 @@ function(_make_version_script target)
       COMMAND ${SHELL} -c "egrep '^\\w' ${header} | grep '${ns}_[a-z0-9_]*(' | grep -v '^typedef' | grep -o '${ns}_[a-z0-9_]*(' | egrep -o '[a-z0-9_]+' | sort -u"
       OUTPUT_VARIABLE sublib_SYMS
       OUTPUT_STRIP_TRAILING_WHITESPACE)
-    string(REPLACE "\n" ";" sublib_SYMS ${sublib_SYMS})
+    string(REPLACE "\n" ";" sublib_SYMS "${sublib_SYMS}")
 
     foreach(sym ${sublib_SYMS})
       file(APPEND ${${target}_VERSION_SCRIPT}
diff --git a/other/BUILD.bazel b/other/BUILD.bazel
index 3239c5c174d..316cedaf8ec 100644
--- a/other/BUILD.bazel
+++ b/other/BUILD.bazel
@@ -31,6 +31,9 @@ cc_binary(
         "//c-toxcore/toxcore:network",
         "//c-toxcore/toxcore:onion",
         "//c-toxcore/toxcore:onion_announce",
+        "//c-toxcore/toxcore:os_memory",
+        "//c-toxcore/toxcore:os_network",
+        "//c-toxcore/toxcore:os_random",
         "//c-toxcore/toxcore:tox",
     ],
 )
diff --git a/other/DHT_bootstrap.c b/other/DHT_bootstrap.c
index 629ec60c7a3..28ddd04c288 100644
--- a/other/DHT_bootstrap.c
+++ b/other/DHT_bootstrap.c
@@ -26,6 +26,9 @@
 #include "../toxcore/network.h"
 #include "../toxcore/onion.h"
 #include "../toxcore/onion_announce.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_network.h"
+#include "../toxcore/os_random.h"
 #include "../toxcore/tox.h"
 
 #define TCP_RELAY_ENABLED
@@ -154,7 +157,7 @@ int main(int argc, char *argv[])
         logger_callback_log(logger, print_log, nullptr, nullptr);
     }
 
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     const uint16_t start_port = PORT;
     const uint16_t end_port = start_port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM);
     DHT *dht = new_dht(logger, mem, rng, ns, mono_time, new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr), true, true);
diff --git a/other/analysis/run-clang-tidy b/other/analysis/run-clang-tidy
index bcdb78f5b84..e6e78bcc582 100755
--- a/other/analysis/run-clang-tidy
+++ b/other/analysis/run-clang-tidy
@@ -79,6 +79,15 @@ CHECKS="$CHECKS,-clang-diagnostic-tautological-pointer-compare"
 # [unreadVariable]
 CHECKS="$CHECKS,-cppcoreguidelines-init-variables"
 
+# False positive on initialising one global using a pointer to another.
+# E.g.:
+#   int a;
+#   int *p = &a;
+# The warning says:
+#   initializing non-local variable with non-const expression depending on
+#   uninitialized non-local variable
+CHECKS="$CHECKS,-cppcoreguidelines-interfaces-global-init"
+
 # Short variable names are used quite a lot, and we don't consider them a
 # readability issue.
 CHECKS="$CHECKS,-readability-identifier-length"
diff --git a/other/analysis/run-cppcheck b/other/analysis/run-cppcheck
index 03c597d709a..d9d24e91e99 100755
--- a/other/analysis/run-cppcheck
+++ b/other/analysis/run-cppcheck
@@ -20,6 +20,8 @@ CPPCHECK+=("--suppress=knownConditionTrueFalse")
 CPPCHECK+=("--suppress=missingIncludeSystem")
 # TODO(iphydf): Maybe fix?
 CPPCHECK+=("--suppress=signConversion")
+# We have suppressions in the code for the misra extension.
+CPPCHECK+=("--suppress=unmatchedSuppression")
 
 # We use this for VLAs.
 CPPCHECK_CXX+=("--suppress=allocaCalled")
diff --git a/other/bootstrap_daemon/BUILD.bazel b/other/bootstrap_daemon/BUILD.bazel
index 4c65660548f..06fcb95f1e2 100644
--- a/other/bootstrap_daemon/BUILD.bazel
+++ b/other/bootstrap_daemon/BUILD.bazel
@@ -25,6 +25,9 @@ cc_binary(
         "//c-toxcore/toxcore:network",
         "//c-toxcore/toxcore:onion",
         "//c-toxcore/toxcore:onion_announce",
+        "//c-toxcore/toxcore:os_memory",
+        "//c-toxcore/toxcore:os_network",
+        "//c-toxcore/toxcore:os_random",
         "//c-toxcore/toxcore:tox",
         "@libconfig",
     ],
diff --git a/other/bootstrap_daemon/docker/Dockerfile b/other/bootstrap_daemon/docker/Dockerfile
index 3d150e02f61..b18f3346ad1 100644
--- a/other/bootstrap_daemon/docker/Dockerfile
+++ b/other/bootstrap_daemon/docker/Dockerfile
@@ -27,7 +27,9 @@ COPY other/DHT_bootstrap.c other/
 COPY other/pkgconfig other/pkgconfig
 COPY other/rpm other/rpm
 COPY testing/misc_tools.[ch] testing/
+COPY tox.h ./
 COPY toxcore toxcore
+COPY toxencryptsave.h ./
 COPY toxencryptsave toxencryptsave
 COPY third_party third_party
 COPY CMakeLists.txt so.version ./
diff --git a/other/bootstrap_daemon/src/log_backend_stdout.h b/other/bootstrap_daemon/src/log_backend_stdout.h
index bb6d2cc457e..3e2868c4916 100644
--- a/other/bootstrap_daemon/src/log_backend_stdout.h
+++ b/other/bootstrap_daemon/src/log_backend_stdout.h
@@ -12,7 +12,7 @@
 
 #include <stdarg.h>
 
-#include "../../../toxcore/attributes.h"
+#include "../../../toxcore/tox_attributes.h"
 #include "log.h"
 
 void log_backend_stdout_write(LOG_LEVEL level, const char *format, va_list args) GNU_PRINTF(2, 0);
diff --git a/other/bootstrap_daemon/src/log_backend_syslog.h b/other/bootstrap_daemon/src/log_backend_syslog.h
index 1538c92d9c6..07c5f83e64a 100644
--- a/other/bootstrap_daemon/src/log_backend_syslog.h
+++ b/other/bootstrap_daemon/src/log_backend_syslog.h
@@ -12,7 +12,7 @@
 
 #include <stdarg.h>
 
-#include "../../../toxcore/attributes.h"
+#include "../../../toxcore/tox_attributes.h"
 #include "log.h"
 
 void log_backend_syslog_open(void);
diff --git a/other/bootstrap_daemon/src/tox-bootstrapd.c b/other/bootstrap_daemon/src/tox-bootstrapd.c
index 09a54df5d16..ca82481d3d3 100644
--- a/other/bootstrap_daemon/src/tox-bootstrapd.c
+++ b/other/bootstrap_daemon/src/tox-bootstrapd.c
@@ -41,6 +41,9 @@
 #include "../../../toxcore/network.h"
 #include "../../../toxcore/onion.h"
 #include "../../../toxcore/onion_announce.h"
+#include "../../../toxcore/os_memory.h"
+#include "../../../toxcore/os_network.h"
+#include "../../../toxcore/os_random.h"
 
 // misc
 #include "../../bootstrap_node_packets.h"
@@ -321,7 +324,7 @@ int main(int argc, char *argv[])
         }
     }
 
-    Mono_Time *const mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *const mono_time = mono_time_new(mem, nullptr);
 
     if (mono_time == nullptr) {
         log_write(LOG_LEVEL_ERROR, "Couldn't initialize monotonic timer. Exiting.\n");
diff --git a/other/docker/modules/check b/other/docker/modules/check
index e62be62c8ae..5e5c041da28 100755
--- a/other/docker/modules/check
+++ b/other/docker/modules/check
@@ -196,6 +196,7 @@ def main() -> None:
     for src in sorted(
             set(srcs) - set([
                 # TODO(iphydf): Figure out what's wrong here.
+                "toxcore/bin_pack_test.cc",
                 "toxcore/crypto_core_test.cc",
                 "toxcore/group_announce_test.cc",
                 "toxcore/group_moderation_test.cc",
diff --git a/testing/BUILD.bazel b/testing/BUILD.bazel
index b8c1a2ba54b..77c9c23a4a8 100644
--- a/testing/BUILD.bazel
+++ b/testing/BUILD.bazel
@@ -87,6 +87,9 @@ cc_binary(
         "//c-toxcore/toxcore:Messenger",
         "//c-toxcore/toxcore:ccompat",
         "//c-toxcore/toxcore:mono_time",
+        "//c-toxcore/toxcore:os_memory",
+        "//c-toxcore/toxcore:os_network",
+        "//c-toxcore/toxcore:os_random",
     ],
 )
 
diff --git a/testing/Messenger_test.c b/testing/Messenger_test.c
index 9e6484e86ce..20fa856eada 100644
--- a/testing/Messenger_test.c
+++ b/testing/Messenger_test.c
@@ -35,6 +35,9 @@
 #include "../toxcore/Messenger.h"
 #include "../toxcore/ccompat.h"
 #include "../toxcore/mono_time.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_network.h"
+#include "../toxcore/os_random.h"
 #include "misc_tools.h"
 
 static void print_message(Messenger *m, uint32_t friendnumber, unsigned int type, const uint8_t *string, size_t length,
@@ -93,7 +96,7 @@ int main(int argc, char *argv[])
     }
 
     const Memory *mem = os_memory();
-    Mono_Time *const mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *const mono_time = mono_time_new(mem, nullptr);
 
     if (mono_time == nullptr) {
         fputs("Failed to allocate monotonic timer datastructure\n", stderr);
diff --git a/testing/fuzzing/BUILD.bazel b/testing/fuzzing/BUILD.bazel
index 4c9c0a1e31a..5b0a5c7240d 100644
--- a/testing/fuzzing/BUILD.bazel
+++ b/testing/fuzzing/BUILD.bazel
@@ -12,7 +12,13 @@ cc_library(
     deps = [
         "//c-toxcore/toxcore:crypto_core",
         "//c-toxcore/toxcore:network",
+        "//c-toxcore/toxcore:os_network",
         "//c-toxcore/toxcore:tox",
+        "//c-toxcore/toxcore:tox_memory",
+        "//c-toxcore/toxcore:tox_network",
+        "//c-toxcore/toxcore:tox_random",
+        "//c-toxcore/toxcore:tox_system",
+        "//c-toxcore/toxcore:tox_time",
     ],
 )
 
diff --git a/testing/fuzzing/bootstrap_fuzz_test.cc b/testing/fuzzing/bootstrap_fuzz_test.cc
index 4a784f846a0..f25e24319d0 100644
--- a/testing/fuzzing/bootstrap_fuzz_test.cc
+++ b/testing/fuzzing/bootstrap_fuzz_test.cc
@@ -104,6 +104,7 @@ void TestBootstrap(Fuzz_Data &input)
 
     Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
     assert(opts != nullptr);
+    tox_options_set_operating_system(opts.get(), sys.sys.get());
 
     tox_options_set_log_callback(opts.get(),
         [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
@@ -133,12 +134,8 @@ void TestBootstrap(Fuzz_Data &input)
         tox_options_set_tcp_port(opts.get(), 33445);
     }
 
-    Tox_Options_Testing tox_options_testing;
-    tox_options_testing.operating_system = sys.sys.get();
-
     Tox_Err_New error_new;
-    Tox_Err_New_Testing error_new_testing;
-    Tox *tox = tox_new_testing(opts.get(), &error_new, &tox_options_testing, &error_new_testing);
+    Tox *tox = tox_new(opts.get(), &error_new);
 
     if (tox == nullptr) {
         // It might fail, because some I/O happens in tox_new, and the fuzzer
@@ -147,7 +144,6 @@ void TestBootstrap(Fuzz_Data &input)
     }
 
     assert(error_new == TOX_ERR_NEW_OK);
-    assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
 
     uint8_t pub_key[TOX_PUBLIC_KEY_SIZE] = {0};
 
diff --git a/testing/fuzzing/e2e_fuzz_test.cc b/testing/fuzzing/e2e_fuzz_test.cc
index df4e03c6982..e81139e1baa 100644
--- a/testing/fuzzing/e2e_fuzz_test.cc
+++ b/testing/fuzzing/e2e_fuzz_test.cc
@@ -138,6 +138,7 @@ void TestEndToEnd(Fuzz_Data &input)
 
     Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
     assert(opts != nullptr);
+    tox_options_set_operating_system(opts.get(), sys.sys.get());
     tox_options_set_local_discovery_enabled(opts.get(), false);
 
     tox_options_set_log_callback(opts.get(),
@@ -150,21 +151,11 @@ void TestEndToEnd(Fuzz_Data &input)
             }
         });
 
-    Tox_Options_Testing tox_options_testing;
-    tox_options_testing.operating_system = sys.sys.get();
-
     Tox_Err_New error_new;
-    Tox_Err_New_Testing error_new_testing;
-    Tox *tox = tox_new_testing(opts.get(), &error_new, &tox_options_testing, &error_new_testing);
-
-    if (tox == nullptr) {
-        // It might fail, because some I/O happens in tox_new, and the fuzzer
-        // might do things that make that I/O fail.
-        return;
-    }
+    Tox *tox = tox_new(opts.get(), &error_new);
 
+    // tox_new never fails, because we execute a recorded successful trace.
     assert(error_new == TOX_ERR_NEW_OK);
-    assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
 
     tox_events_init(tox);
 
diff --git a/testing/fuzzing/fuzz_support.cc b/testing/fuzzing/fuzz_support.cc
index 7f949701609..6f3e0866095 100644
--- a/testing/fuzzing/fuzz_support.cc
+++ b/testing/fuzzing/fuzz_support.cc
@@ -24,7 +24,12 @@
 
 #include "../../toxcore/crypto_core.h"
 #include "../../toxcore/network.h"
-#include "../../toxcore/tox_private.h"
+#include "../../toxcore/os_network_impl.h"
+#include "../../toxcore/tox_memory_impl.h"
+#include "../../toxcore/tox_network_impl.h"
+#include "../../toxcore/tox_random_impl.h"
+#include "../../toxcore/tox_system_impl.h"
+#include "../../toxcore/tox_time_impl.h"
 #include "func_conversion.hh"
 
 // TODO(iphydf): Put this somewhere shared.
@@ -33,12 +38,14 @@ struct Network_Addr {
     size_t size;
 };
 
-System::System(std::unique_ptr<Tox_System> in_sys, std::unique_ptr<Memory> in_mem,
-    std::unique_ptr<Network> in_ns, std::unique_ptr<Random> in_rng)
+System::System(std::unique_ptr<Tox_System> in_sys, std::unique_ptr<Tox_Memory> in_mem,
+    std::unique_ptr<Tox_Network> in_ns, std::unique_ptr<Tox_Random> in_rng,
+    std::unique_ptr<Tox_Time> in_tm)
     : sys(std::move(in_sys))
     , mem(std::move(in_mem))
     , ns(std::move(in_ns))
     , rng(std::move(in_rng))
+    , tm(std::move(in_tm))
 {
 }
 System::System(System &&) = default;
@@ -94,16 +101,11 @@ static void *alloc_common(const char *func, std::size_t size, Fuzz_Data &data, A
     return report_alloc("tox1", func, size, Func(args...));
 }
 
-static constexpr Memory_Funcs fuzz_memory_funcs = {
+static constexpr Tox_Memory_Funcs fuzz_memory_funcs = {
     /* .malloc = */
     ![](Fuzz_System *self, uint32_t size) {
         return alloc_common<decltype(std::malloc), std::malloc>("malloc", size, self->data, size);
     },
-    /* .calloc = */
-    ![](Fuzz_System *self, uint32_t nmemb, uint32_t size) {
-        return alloc_common<decltype(std::calloc), std::calloc>(
-            "calloc", nmemb * size, self->data, nmemb, size);
-    },
     /* .realloc = */
     ![](Fuzz_System *self, void *ptr, uint32_t size) {
         return alloc_common<decltype(std::realloc), std::realloc>(
@@ -113,7 +115,7 @@ static constexpr Memory_Funcs fuzz_memory_funcs = {
     ![](Fuzz_System *self, void *ptr) { std::free(ptr); },
 };
 
-static constexpr Network_Funcs fuzz_network_funcs = {
+static constexpr Tox_Network_Funcs fuzz_network_funcs = {
     /* .close = */ ![](Fuzz_System *self, Socket sock) { return 0; },
     /* .accept = */ ![](Fuzz_System *self, Socket sock) { return Socket{1337}; },
     /* .bind = */ ![](Fuzz_System *self, Socket sock, const Network_Addr *addr) { return 0; },
@@ -134,16 +136,14 @@ static constexpr Network_Funcs fuzz_network_funcs = {
     /* .recvfrom = */
     ![](Fuzz_System *self, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr) {
         assert(sock.value == 42 || sock.value == 1337);
-
-        addr->addr = sockaddr_storage{};
-        // Dummy Addr
-        addr->addr.ss_family = AF_INET;
+        assert(addr != nullptr);
 
         // We want an AF_INET address with dummy values
-        sockaddr_in *addr_in = reinterpret_cast<sockaddr_in *>(&addr->addr);
-        addr_in->sin_port = htons(33446);
-        addr_in->sin_addr.s_addr = htonl(0x7f000002);  // 127.0.0.2
-        addr->size = sizeof(struct sockaddr);
+        sockaddr_in addr_in{};
+        addr_in.sin_port = htons(33446);
+        addr_in.sin_addr.s_addr = htonl(0x7f000002);  // 127.0.0.2
+
+        net_addr_set(addr, &addr_in, sizeof(addr_in));
 
         return recv_common(self->data, buf, len);
     },
@@ -172,7 +172,7 @@ static constexpr Network_Funcs fuzz_network_funcs = {
     },
 };
 
-static constexpr Random_Funcs fuzz_random_funcs = {
+static constexpr Tox_Random_Funcs fuzz_random_funcs = {
     /* .random_bytes = */
     ![](Fuzz_System *self, uint8_t *bytes, size_t length) {
         // Amount of data is limited
@@ -193,7 +193,7 @@ static constexpr Random_Funcs fuzz_random_funcs = {
     ![](Fuzz_System *self, uint32_t upper_bound) {
         uint32_t randnum = 0;
         if (upper_bound > 0) {
-            self->rng->funcs->random_bytes(
+            self->rng->funcs->bytes_callback(
                 self, reinterpret_cast<uint8_t *>(&randnum), sizeof(randnum));
             randnum %= upper_bound;
         }
@@ -201,34 +201,37 @@ static constexpr Random_Funcs fuzz_random_funcs = {
     },
 };
 
+static constexpr Tox_Time_Funcs fuzz_time_funcs = {
+    /* .monotonic = */
+    ![](Fuzz_System *self) { return self->clock; },
+};
+
 Fuzz_System::Fuzz_System(Fuzz_Data &input)
     : System{
         std::make_unique<Tox_System>(),
         std::make_unique<Memory>(Memory{&fuzz_memory_funcs, this}),
         std::make_unique<Network>(Network{&fuzz_network_funcs, this}),
         std::make_unique<Random>(Random{&fuzz_random_funcs, this}),
+        std::make_unique<Tox_Time>(Tox_Time{&fuzz_time_funcs, this}),
     }
     , data(input)
 {
-    sys->mono_time_callback = [](void *self) { return static_cast<Fuzz_System *>(self)->clock; };
-    sys->mono_time_user_data = this;
     sys->mem = mem.get();
     sys->ns = ns.get();
     sys->rng = rng.get();
+    sys->tm = tm.get();
 }
 
-static constexpr Memory_Funcs null_memory_funcs = {
+static constexpr Tox_Memory_Funcs null_memory_funcs = {
     /* .malloc = */
     ![](Null_System *self, uint32_t size) { return std::malloc(size); },
-    /* .calloc = */
-    ![](Null_System *self, uint32_t nmemb, uint32_t size) { return std::calloc(nmemb, size); },
     /* .realloc = */
     ![](Null_System *self, void *ptr, uint32_t size) { return std::realloc(ptr, size); },
     /* .free = */
     ![](Null_System *self, void *ptr) { std::free(ptr); },
 };
 
-static constexpr Network_Funcs null_network_funcs = {
+static constexpr Tox_Network_Funcs null_network_funcs = {
     /* .close = */ ![](Null_System *self, Socket sock) { return 0; },
     /* .accept = */ ![](Null_System *self, Socket sock) { return Socket{1337}; },
     /* .bind = */ ![](Null_System *self, Socket sock, const Network_Addr *addr) { return 0; },
@@ -277,7 +280,7 @@ static uint64_t simple_rng(uint64_t &seed)
     return seed;
 }
 
-static constexpr Random_Funcs null_random_funcs = {
+static constexpr Tox_Random_Funcs null_random_funcs = {
     /* .random_bytes = */
     ![](Null_System *self, uint8_t *bytes, size_t length) {
         for (size_t i = 0; i < length; ++i) {
@@ -293,39 +296,24 @@ static constexpr Random_Funcs null_random_funcs = {
 Null_System::Null_System()
     : System{
         std::make_unique<Tox_System>(),
-        std::make_unique<Memory>(Memory{&null_memory_funcs, this}),
-        std::make_unique<Network>(Network{&null_network_funcs, this}),
-        std::make_unique<Random>(Random{&null_random_funcs, this}),
+        std::make_unique<Tox_Memory>(Tox_Memory{&null_memory_funcs, this}),
+        std::make_unique<Tox_Network>(Tox_Network{&null_network_funcs, this}),
+        std::make_unique<Tox_Random>(Tox_Random{&null_random_funcs, this}),
+        std::make_unique<Tox_Time>(Tox_Time{&fuzz_time_funcs, this}),
     }
 {
-    sys->mono_time_callback = [](void *self) { return static_cast<Null_System *>(self)->clock; };
-    sys->mono_time_user_data = this;
     sys->mem = mem.get();
     sys->ns = ns.get();
     sys->rng = rng.get();
+    sys->tm = tm.get();
 }
 
-static uint16_t get_port(const Network_Addr *addr)
-{
-    if (addr->addr.ss_family == AF_INET6) {
-        return reinterpret_cast<const sockaddr_in6 *>(&addr->addr)->sin6_port;
-    } else {
-        assert(addr->addr.ss_family == AF_INET);
-        return reinterpret_cast<const sockaddr_in *>(&addr->addr)->sin_port;
-    }
-}
-
-static constexpr Memory_Funcs record_memory_funcs = {
+static constexpr Tox_Memory_Funcs record_memory_funcs = {
     /* .malloc = */
     ![](Record_System *self, uint32_t size) {
         self->push(true);
         return report_alloc(self->name_, "malloc", size, std::malloc(size));
     },
-    /* .calloc = */
-    ![](Record_System *self, uint32_t nmemb, uint32_t size) {
-        self->push(true);
-        return report_alloc(self->name_, "calloc", nmemb * size, std::calloc(nmemb, size));
-    },
     /* .realloc = */
     ![](Record_System *self, void *ptr, uint32_t size) {
         self->push(true);
@@ -335,12 +323,13 @@ static constexpr Memory_Funcs record_memory_funcs = {
     ![](Record_System *self, void *ptr) { std::free(ptr); },
 };
 
-static constexpr Network_Funcs record_network_funcs = {
+static constexpr Tox_Network_Funcs record_network_funcs = {
     /* .close = */ ![](Record_System *self, Socket sock) { return 0; },
     /* .accept = */ ![](Record_System *self, Socket sock) { return Socket{2}; },
     /* .bind = */
     ![](Record_System *self, Socket sock, const Network_Addr *addr) {
-        const uint16_t port = get_port(addr);
+        assert(addr != nullptr);
+        const uint16_t port = net_addr_get_port(addr);
         if (self->global_.bound.find(port) != self->global_.bound.end()) {
             errno = EADDRINUSE;
             return -1;
@@ -361,6 +350,7 @@ static constexpr Network_Funcs record_network_funcs = {
     /* .recvfrom = */
     ![](Record_System *self, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr) {
         assert(sock.value == 42);
+        assert(addr != nullptr);
         if (self->recvq.empty()) {
             self->push("\xff\xff");
             errno = EWOULDBLOCK;
@@ -374,15 +364,13 @@ static constexpr Network_Funcs record_network_funcs = {
         const size_t recvlen = std::min(len, packet.size());
         std::copy(packet.begin(), packet.end(), buf);
 
-        addr->addr = sockaddr_storage{};
-        // Dummy Addr
-        addr->addr.ss_family = AF_INET;
-
         // We want an AF_INET address with dummy values
-        sockaddr_in *addr_in = reinterpret_cast<sockaddr_in *>(&addr->addr);
-        addr_in->sin_port = from;
-        addr_in->sin_addr.s_addr = htonl(0x7f000002);  // 127.0.0.2
-        addr->size = sizeof(struct sockaddr);
+        sockaddr_in addr_in{};
+        addr_in.sin_family = AF_INET;
+        addr_in.sin_port = from;
+        addr_in.sin_addr.s_addr = htonl(0x7f000002);  // 127.0.0.2
+
+        net_addr_set(addr, &addr_in, sizeof(addr_in));
 
         assert(recvlen > 0 && recvlen <= INT_MAX);
         self->push(uint8_t(recvlen >> 8));
@@ -403,7 +391,8 @@ static constexpr Network_Funcs record_network_funcs = {
     ![](Record_System *self, Socket sock, const uint8_t *buf, size_t len,
          const Network_Addr *addr) {
         assert(sock.value == 42);
-        auto backend = self->global_.bound.find(get_port(addr));
+        assert(addr != nullptr);
+        auto backend = self->global_.bound.find(net_addr_get_port(addr));
         assert(backend != self->global_.bound.end());
         backend->second->receive(self->port, buf, len);
         return static_cast<int>(len);
@@ -421,7 +410,7 @@ static constexpr Network_Funcs record_network_funcs = {
          size_t optlen) { return 0; },
 };
 
-static constexpr Random_Funcs record_random_funcs = {
+static constexpr Tox_Random_Funcs record_random_funcs = {
     /* .random_bytes = */
     ![](Record_System *self, uint8_t *bytes, size_t length) {
         for (size_t i = 0; i < length; ++i) {
@@ -434,7 +423,7 @@ static constexpr Random_Funcs record_random_funcs = {
         }
     },
     /* .random_uniform = */
-    fuzz_random_funcs.random_uniform,
+    fuzz_random_funcs.uniform_callback,
 };
 
 Record_System::Record_System(Global &global, uint64_t seed, const char *name)
@@ -443,16 +432,16 @@ Record_System::Record_System(Global &global, uint64_t seed, const char *name)
         std::make_unique<Memory>(Memory{&record_memory_funcs, this}),
         std::make_unique<Network>(Network{&record_network_funcs, this}),
         std::make_unique<Random>(Random{&record_random_funcs, this}),
+        std::make_unique<Tox_Time>(Tox_Time{&fuzz_time_funcs, this}),
     }
     , global_(global)
     , seed_(seed)
     , name_(name)
 {
-    sys->mono_time_callback = [](void *self) { return static_cast<Record_System *>(self)->clock; };
-    sys->mono_time_user_data = this;
     sys->mem = mem.get();
     sys->ns = ns.get();
     sys->rng = rng.get();
+    sys->tm = tm.get();
 }
 
 void Record_System::receive(uint16_t send_port, const uint8_t *buf, size_t len)
diff --git a/testing/fuzzing/fuzz_support.hh b/testing/fuzzing/fuzz_support.hh
index c9eca0a0fbc..ad7a9f1da8e 100644
--- a/testing/fuzzing/fuzz_support.hh
+++ b/testing/fuzzing/fuzz_support.hh
@@ -16,7 +16,11 @@
 #include <vector>
 
 #include "../../toxcore/tox.h"
-#include "../../toxcore/tox_private.h"
+#include "../../toxcore/tox_memory.h"
+#include "../../toxcore/tox_network.h"
+#include "../../toxcore/tox_random.h"
+#include "../../toxcore/tox_system.h"
+#include "../../toxcore/tox_time.h"
 
 struct Fuzz_Data {
     static constexpr bool DEBUG = false;
@@ -187,9 +191,10 @@ void fuzz_select_target(const uint8_t *data, std::size_t size)
     return Fuzz_Target_Selector<Args...>::select(selector, input);
 }
 
-struct Memory;
-struct Network;
-struct Random;
+struct Tox_Memory;
+struct Tox_Network;
+struct Tox_Random;
+struct Tox_Time;
 
 struct System {
     /** @brief Deterministic system clock for this instance.
@@ -205,12 +210,14 @@ struct System {
     uint64_t clock = 1000000000;
 
     std::unique_ptr<Tox_System> sys;
-    std::unique_ptr<Memory> mem;
-    std::unique_ptr<Network> ns;
-    std::unique_ptr<Random> rng;
-
-    System(std::unique_ptr<Tox_System> sys, std::unique_ptr<Memory> mem,
-        std::unique_ptr<Network> ns, std::unique_ptr<Random> rng);
+    std::unique_ptr<Tox_Memory> mem;
+    std::unique_ptr<Tox_Network> ns;
+    std::unique_ptr<Tox_Random> rng;
+    std::unique_ptr<Tox_Time> tm;
+
+    System(std::unique_ptr<Tox_System> sys, std::unique_ptr<Tox_Memory> mem,
+        std::unique_ptr<Tox_Network> ns, std::unique_ptr<Tox_Random> rng,
+        std::unique_ptr<Tox_Time> tm);
     System(System &&);
 
     // Not inline because sizeof of the above 2 structs is not known everywhere.
diff --git a/testing/fuzzing/protodump.cc b/testing/fuzzing/protodump.cc
index 28df05a02a1..b770d8cd9f1 100644
--- a/testing/fuzzing/protodump.cc
+++ b/testing/fuzzing/protodump.cc
@@ -30,8 +30,8 @@
 #include "../../toxcore/tox.h"
 #include "../../toxcore/tox_dispatch.h"
 #include "../../toxcore/tox_events.h"
+#include "../../toxcore/tox_impl.h"
 #include "../../toxcore/tox_private.h"
-#include "../../toxcore/tox_struct.h"
 #include "../../toxcore/util.h"
 #include "fuzz_support.hh"
 
@@ -195,16 +195,13 @@ void RecordBootstrap(const char *init, const char *bootstrap)
         });
 
     Tox_Err_New error_new;
-    Tox_Err_New_Testing error_new_testing;
-    Tox_Options_Testing tox_options_testing;
 
     Record_System sys1(global, 4, "tox1");  // fair dice roll
     tox_options_set_log_user_data(opts, &sys1);
-    tox_options_testing.operating_system = sys1.sys.get();
-    Tox *tox1 = tox_new_testing(opts, &error_new, &tox_options_testing, &error_new_testing);
+    tox_options_set_operating_system(opts, sys1.sys.get());
+    Tox *tox1 = tox_new(opts, &error_new);
     assert(tox1 != nullptr);
     assert(error_new == TOX_ERR_NEW_OK);
-    assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
     std::array<uint8_t, TOX_ADDRESS_SIZE> address1;
     tox_self_get_address(tox1, address1.data());
     std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> pk1;
@@ -214,11 +211,10 @@ void RecordBootstrap(const char *init, const char *bootstrap)
 
     Record_System sys2(global, 5, "tox2");  // unfair dice roll
     tox_options_set_log_user_data(opts, &sys2);
-    tox_options_testing.operating_system = sys2.sys.get();
-    Tox *tox2 = tox_new_testing(opts, &error_new, &tox_options_testing, &error_new_testing);
+    tox_options_set_operating_system(opts, sys2.sys.get());
+    Tox *tox2 = tox_new(opts, &error_new);
     assert(tox2 != nullptr);
     assert(error_new == TOX_ERR_NEW_OK);
-    assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
     std::array<uint8_t, TOX_ADDRESS_SIZE> address2;
     tox_self_get_address(tox2, address2.data());
     std::array<uint8_t, TOX_PUBLIC_KEY_SIZE> pk2;
diff --git a/testing/fuzzing/protodump_reduce.cc b/testing/fuzzing/protodump_reduce.cc
index 820a3fc7b88..ae9676ca31b 100644
--- a/testing/fuzzing/protodump_reduce.cc
+++ b/testing/fuzzing/protodump_reduce.cc
@@ -142,11 +142,9 @@ void TestEndToEnd(Fuzz_Data &input)
 
     Ptr<Tox_Options> opts(tox_options_new(nullptr), tox_options_free);
     assert(opts != nullptr);
+    tox_options_set_operating_system(opts.get(), sys.sys.get());
     tox_options_set_local_discovery_enabled(opts.get(), false);
 
-    Tox_Options_Testing tox_options_testing;
-    tox_options_testing.operating_system = sys.sys.get();
-
     tox_options_set_log_callback(opts.get(),
         [](Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
             const char *message, void *user_data) {
@@ -158,8 +156,7 @@ void TestEndToEnd(Fuzz_Data &input)
         });
 
     Tox_Err_New error_new;
-    Tox_Err_New_Testing error_new_testing;
-    Tox *tox = tox_new_testing(opts.get(), &error_new, &tox_options_testing, &error_new_testing);
+    Tox *tox = tox_new(opts.get(), &error_new);
 
     if (tox == nullptr) {
         // It might fail, because some I/O happens in tox_new, and the fuzzer
@@ -168,7 +165,6 @@ void TestEndToEnd(Fuzz_Data &input)
     }
 
     assert(error_new == TOX_ERR_NEW_OK);
-    assert(error_new_testing == TOX_ERR_NEW_TESTING_OK);
 
     tox_events_init(tox);
 
diff --git a/testing/fuzzing/toxsave_fuzz_test.cc b/testing/fuzzing/toxsave_fuzz_test.cc
index f48c2bf0131..2cb134cbc1f 100644
--- a/testing/fuzzing/toxsave_fuzz_test.cc
+++ b/testing/fuzzing/toxsave_fuzz_test.cc
@@ -20,15 +20,14 @@ void TestSaveDataLoading(Fuzz_Data &input)
     const size_t savedata_size = input.size();
     CONSUME_OR_RETURN(const uint8_t *savedata, input, savedata_size);
 
+    Null_System sys;
+    tox_options_set_operating_system(tox_options, sys.sys.get());
+
     // pass test data to Tox
     tox_options_set_savedata_data(tox_options, savedata, savedata_size);
     tox_options_set_savedata_type(tox_options, TOX_SAVEDATA_TYPE_TOX_SAVE);
 
-    Tox_Options_Testing tox_options_testing;
-    Null_System sys;
-    tox_options_testing.operating_system = sys.sys.get();
-
-    Tox *tox = tox_new_testing(tox_options, nullptr, &tox_options_testing, nullptr);
+    Tox *tox = tox_new(tox_options, nullptr);
     tox_options_free(tox_options);
     if (tox == nullptr) {
         // Tox save was invalid, we're finished here
diff --git a/tox.h b/tox.h
new file mode 100644
index 00000000000..4209883cf60
--- /dev/null
+++ b/tox.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#include "toxcore/tox.h"
diff --git a/toxav.h b/toxav.h
new file mode 100644
index 00000000000..83e5daa8e64
--- /dev/null
+++ b/toxav.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#include "toxav/toxav.h"
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
index 6d34135f3c8..47dfba5d083 100644
--- a/toxav/Makefile.inc
+++ b/toxav/Makefile.inc
@@ -1,8 +1,9 @@
 if BUILD_AV
 
 lib_LTLIBRARIES +=  libtoxav.la
-                    libtoxav_la_include_HEADERS = ../toxav/toxav.h
-                    libtoxav_la_includedir = $(includedir)/tox
+
+libtoxav_la_include_HEADERS = ../toxav/toxav.h
+libtoxav_la_includedir = $(includedir)/tox/toxav
 
 libtoxav_la_SOURCES = ../toxav/rtp.h \
                     ../toxav/rtp.c \
diff --git a/toxav/groupav.c b/toxav/groupav.c
index b937d0296fe..ce8f254474e 100644
--- a/toxav/groupav.c
+++ b/toxav/groupav.c
@@ -10,7 +10,7 @@
 #include "../toxcore/ccompat.h"
 #include "../toxcore/logger.h"
 #include "../toxcore/mono_time.h"
-#include "../toxcore/tox_struct.h"
+#include "../toxcore/tox_impl.h"
 #include "../toxcore/util.h"
 
 #define GROUP_JBUF_SIZE 6
diff --git a/toxav/rtp_test.cc b/toxav/rtp_test.cc
index 674bc4b46c0..24e5244804d 100644
--- a/toxav/rtp_test.cc
+++ b/toxav/rtp_test.cc
@@ -3,6 +3,7 @@
 #include <gtest/gtest.h>
 
 #include "../toxcore/crypto_core.h"
+#include "../toxcore/os_random.h"
 
 namespace {
 
diff --git a/toxav/toxav.c b/toxav/toxav.c
index b69ce151f30..48b54a96bbd 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -19,8 +19,8 @@
 #include "../toxcore/net_crypto.h"
 #include "../toxcore/network.h"
 #include "../toxcore/tox.h"
+#include "../toxcore/tox_impl.h" // IWYU pragma: keep
 #include "../toxcore/tox_private.h"
-#include "../toxcore/tox_struct.h"  // IWYU pragma: keep
 #include "../toxcore/util.h"
 
 // TODO(zoff99): don't hardcode this, let the application choose it
@@ -226,7 +226,7 @@ ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error)
     rtp_allow_receiving(av->tox);
     bwc_allow_receiving(av->tox);
 
-    av->toxav_mono_time = mono_time_new(tox->sys.mem, nullptr, nullptr);
+    av->toxav_mono_time = mono_time_new(tox->sys.mem, nullptr);
 
     if (av->msi == nullptr) {
         pthread_mutex_destroy(av->mutex);
diff --git a/toxav/toxav.h b/toxav/toxav.h
index b77bf06f8fe..546e332bb77 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -59,22 +59,13 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "../toxcore/tox.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/**
- * External Tox type.
- */
-#ifndef APIGEN_IGNORE
-#ifndef TOX_DEFINED
-#define TOX_DEFINED
-typedef struct Tox Tox;
-#endif /* !TOX_DEFINED */
-#endif /* !APIGEN_IGNORE */
-
 #ifndef TOXAV_DEFINED
-#define TOXAV_DEFINED
 /**
  * @brief The ToxAV instance type.
  *
diff --git a/toxav/toxav_old.c b/toxav/toxav_old.c
index 223f5c45b85..5e8116be298 100644
--- a/toxav/toxav_old.c
+++ b/toxav/toxav_old.c
@@ -8,7 +8,7 @@
  */
 #include "toxav.h"
 
-#include "../toxcore/tox_struct.h"
+#include "../toxcore/tox_impl.h"
 #include "groupav.h"
 
 int toxav_add_av_groupchat(Tox *tox, audio_data_cb *audio_callback, void *userdata)
diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel
index 8bbd870f694..d9529f375af 100644
--- a/toxcore/BUILD.bazel
+++ b/toxcore/BUILD.bazel
@@ -1,12 +1,33 @@
 load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
 load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
 
-exports_files(
+sh_library(
+    name = "public",
     srcs = [
+        "os_log.h",
+        "os_memory.h",
+        "os_network.h",
+        "os_random.h",
+        "os_system.h",
+        "os_time.h",
         "tox.h",
+        "tox_attributes.h",
         "tox_dispatch.h",
         "tox_events.h",
-        "tox_private.h",
+        "tox_log.h",
+        "tox_log_impl.h",
+        "tox_log_level.h",
+        "tox_memory.h",
+        "tox_memory_impl.h",
+        "tox_network.h",
+        "tox_network_impl.h",
+        "tox_options.h",
+        "tox_random.h",
+        "tox_random_impl.h",
+        "tox_system.h",
+        "tox_system_impl.h",
+        "tox_time.h",
+        "tox_time_impl.h",
     ],
     visibility = ["//c-toxcore:__subpackages__"],
 )
@@ -37,6 +58,12 @@ cc_library(
     visibility = ["//c-toxcore:__subpackages__"],
 )
 
+cc_library(
+    name = "tox_attributes",
+    hdrs = ["tox_attributes.h"],
+    visibility = ["//c-toxcore:__subpackages__"],
+)
+
 cc_library(
     name = "ccompat",
     srcs = ["ccompat.c"],
@@ -45,6 +72,190 @@ cc_library(
     deps = [":attributes"],
 )
 
+cc_library(
+    name = "tox_memory",
+    srcs = ["tox_memory.c"],
+    hdrs = [
+        "tox_memory.h",
+        "tox_memory_impl.h",
+    ],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":ccompat",
+        ":tox_attributes",
+    ],
+)
+
+cc_library(
+    name = "tox_log_level",
+    srcs = ["tox_log_level.c"],
+    hdrs = ["tox_log_level.h"],
+    visibility = ["//c-toxcore:__subpackages__"],
+)
+
+cc_library(
+    name = "tox_log",
+    srcs = ["tox_log.c"],
+    hdrs = [
+        "tox_log.h",
+        "tox_log_impl.h",
+    ],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":ccompat",
+        ":tox_attributes",
+        ":tox_log_level",
+        ":tox_memory",
+    ],
+)
+
+cc_library(
+    name = "tox_network",
+    srcs = ["tox_network.c"],
+    hdrs = [
+        "tox_network.h",
+        "tox_network_impl.h",
+    ],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":ccompat",
+        ":tox_attributes",
+        ":tox_memory",
+    ],
+)
+
+cc_library(
+    name = "tox_random",
+    srcs = ["tox_random.c"],
+    hdrs = [
+        "tox_random.h",
+        "tox_random_impl.h",
+    ],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":ccompat",
+        ":tox_attributes",
+        ":tox_memory",
+    ],
+)
+
+cc_library(
+    name = "tox_time",
+    srcs = ["tox_time.c"],
+    hdrs = [
+        "tox_time.h",
+        "tox_time_impl.h",
+    ],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":ccompat",
+        ":tox_attributes",
+        ":tox_memory",
+    ],
+)
+
+cc_library(
+    name = "tox_system",
+    srcs = ["tox_system.c"],
+    hdrs = [
+        "tox_system.h",
+        "tox_system_impl.h",
+    ],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":ccompat",
+        ":tox_attributes",
+        ":tox_log",
+        ":tox_memory",
+        ":tox_network",
+        ":tox_random",
+        ":tox_time",
+    ],
+)
+
+cc_library(
+    name = "os_log",
+    srcs = ["os_log.c"],
+    hdrs = ["os_log.h"],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":attributes",
+        ":ccompat",
+        ":tox_log",
+    ],
+)
+
+cc_library(
+    name = "os_memory",
+    srcs = ["os_memory.c"],
+    hdrs = ["os_memory.h"],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":attributes",
+        ":ccompat",
+        ":tox_memory",
+    ],
+)
+
+cc_library(
+    name = "os_network",
+    srcs = ["os_network.c"],
+    hdrs = [
+        "os_network.h",
+        "os_network_impl.h",
+    ],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":attributes",
+        ":ccompat",
+        ":mem",
+        ":tox_memory",
+        ":tox_network",
+        "@psocket",
+    ],
+)
+
+cc_library(
+    name = "os_random",
+    srcs = ["os_random.c"],
+    hdrs = ["os_random.h"],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":attributes",
+        ":ccompat",
+        ":tox_random",
+        "@libsodium",
+    ],
+)
+
+cc_library(
+    name = "os_time",
+    srcs = ["os_time.c"],
+    hdrs = ["os_time.h"],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":attributes",
+        ":ccompat",
+        ":tox_time",
+    ],
+)
+
+cc_library(
+    name = "os_system",
+    srcs = ["os_system.c"],
+    hdrs = ["os_system.h"],
+    visibility = ["//c-toxcore:__subpackages__"],
+    deps = [
+        ":ccompat",
+        ":os_log",
+        ":os_memory",
+        ":os_network",
+        ":os_random",
+        ":os_time",
+        ":tox_system",
+    ],
+)
+
 cc_library(
     name = "mem",
     srcs = ["mem.c"],
@@ -53,6 +264,7 @@ cc_library(
     deps = [
         ":attributes",
         ":ccompat",
+        ":tox_memory",
     ],
 )
 
@@ -63,7 +275,9 @@ cc_library(
     hdrs = ["mem_test_util.hh"],
     deps = [
         ":mem",
+        ":os_memory",
         ":test_util",
+        ":tox_memory",
     ],
 )
 
@@ -73,6 +287,7 @@ cc_test(
     srcs = ["mem_test.cc"],
     deps = [
         ":mem",
+        ":os_memory",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
@@ -154,23 +369,6 @@ cc_binary(
     ],
 )
 
-cc_library(
-    name = "logger",
-    srcs = ["logger.c"],
-    hdrs = ["logger.h"],
-    visibility = [
-        "//c-toxcore/auto_tests:__pkg__",
-        "//c-toxcore/other:__pkg__",
-        "//c-toxcore/other/bootstrap_daemon:__pkg__",
-        "//c-toxcore/toxav:__pkg__",
-    ],
-    deps = [
-        ":attributes",
-        ":ccompat",
-        ":mem",
-    ],
-)
-
 cc_library(
     name = "bin_pack",
     srcs = ["bin_pack.c"],
@@ -180,6 +378,7 @@ cc_library(
         ":attributes",
         ":ccompat",
         ":logger",
+        ":mem",
         "//c-toxcore/third_party:cmp",
     ],
 )
@@ -192,6 +391,7 @@ cc_library(
     deps = [
         ":attributes",
         ":ccompat",
+        ":mem",
         "//c-toxcore/third_party:cmp",
     ],
 )
@@ -204,6 +404,7 @@ cc_test(
         ":bin_pack",
         ":bin_unpack",
         ":logger",
+        ":mem_test_util",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
@@ -218,6 +419,7 @@ cc_library(
         ":attributes",
         ":ccompat",
         ":mem",
+        ":tox_random",
         ":util",
         "@libsodium",
     ],
@@ -245,6 +447,7 @@ cc_library(
     deps = [
         ":crypto_core",
         ":test_util",
+        ":tox_random",
     ],
 )
 
@@ -256,7 +459,10 @@ cc_test(
     deps = [
         ":crypto_core",
         ":crypto_core_test_util",
+        ":mem",
         ":mem_test_util",
+        ":os_memory",
+        ":os_random",
         ":util",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
@@ -281,11 +487,29 @@ cc_test(
     deps = [
         ":list",
         ":mem",
+        ":os_memory",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 )
 
+cc_library(
+    name = "logger",
+    srcs = ["logger.c"],
+    hdrs = ["logger.h"],
+    visibility = [
+        "//c-toxcore/auto_tests:__pkg__",
+        "//c-toxcore/other:__pkg__",
+        "//c-toxcore/other/bootstrap_daemon:__pkg__",
+        "//c-toxcore/toxav:__pkg__",
+    ],
+    deps = [
+        ":attributes",
+        ":ccompat",
+        ":mem",
+    ],
+)
+
 cc_library(
     name = "state",
     srcs = ["state.c"],
@@ -312,6 +536,8 @@ cc_library(
         ":attributes",
         ":ccompat",
         ":mem",
+        ":os_time",
+        ":tox_time",
         ":util",
         "@pthread",
     ],
@@ -324,6 +550,8 @@ cc_test(
     deps = [
         ":mem_test_util",
         ":mono_time",
+        ":os_memory",
+        ":tox_time",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
@@ -381,6 +609,8 @@ cc_library(
         ":logger",
         ":mem",
         ":mono_time",
+        ":os_network",
+        ":tox_network",
         ":net_profile",
         ":util",
         "@libsodium",
@@ -398,7 +628,9 @@ cc_library(
         ":crypto_core",
         ":mem",
         ":network",
+        ":os_network",
         ":test_util",
+        ":tox_network",
     ],
 )
 
@@ -448,6 +680,8 @@ cc_test(
         ":crypto_core_test_util",
         ":mem_test_util",
         ":mono_time",
+        ":os_memory",
+        ":os_random",
         ":ping_array",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
@@ -469,6 +703,7 @@ cc_library(
         ":crypto_core",
         ":mem",
         ":network",
+        ":tox_network",
         ":util",
         "@psocket",
     ],
@@ -532,6 +767,7 @@ cc_test(
         ":crypto_core",
         ":crypto_core_test_util",
         ":logger",
+        ":mem",
         ":mem_test_util",
         ":mono_time",
         ":network",
@@ -607,6 +843,8 @@ cc_fuzz_test(
     corpus = ["//tools/toktok-fuzzer/corpus:forwarding_fuzz_test"],
     deps = [
         ":forwarding",
+        ":os_memory",
+        ":os_network",
         "//c-toxcore/testing/fuzzing:fuzz_support",
         "//c-toxcore/testing/fuzzing:fuzz_tox",
     ],
@@ -650,6 +888,7 @@ cc_library(
         ":mem",
         ":net_profile",
         ":network",
+        ":tox_network",
     ],
 )
 
@@ -679,6 +918,7 @@ cc_library(
         ":net_profile",
         ":network",
         ":onion",
+        ":tox_network",
         ":util",
         "@psocket",
     ],
@@ -700,6 +940,7 @@ cc_library(
         ":mono_time",
         ":net_profile",
         ":network",
+        ":tox_network",
         ":util",
     ],
 )
@@ -825,9 +1066,12 @@ cc_test(
         ":crypto_core",
         ":group_announce",
         ":logger",
+        ":mem",
         ":mem_test_util",
         ":mono_time",
         ":network",
+        ":os_memory",
+        ":tox_time",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
@@ -842,6 +1086,7 @@ cc_fuzz_test(
     deps = [
         ":group_announce",
         ":mem_test_util",
+        ":tox_time",
         "//c-toxcore/testing/fuzzing:fuzz_support",
     ],
 )
@@ -974,6 +1219,7 @@ cc_test(
         ":group_moderation",
         ":logger",
         ":mem_test_util",
+        ":os_random",
         ":util",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
@@ -1075,12 +1321,14 @@ cc_library(
     srcs = [
         "tox.c",
         "tox_api.c",
+        "tox_options.c",
         "tox_private.c",
     ],
     hdrs = [
         "tox.h",
+        "tox_impl.h",
+        "tox_options.h",
         "tox_private.h",
-        "tox_struct.h",
     ],
     visibility = ["//c-toxcore:__subpackages__"],
     deps = [
@@ -1101,7 +1349,12 @@ cc_library(
         ":net_profile",
         ":network",
         ":onion_client",
+        ":os_system",
         ":state",
+        ":tox_attributes",
+        ":tox_log",
+        ":tox_log_level",
+        ":tox_system",
         ":util",
         "//c-toxcore/toxencryptsave:defines",
         "@pthread",
@@ -1114,7 +1367,9 @@ cc_test(
     srcs = ["tox_test.cc"],
     deps = [
         ":crypto_core",
+        ":os_random",
         ":tox",
+        ":tox_log",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
@@ -1168,7 +1423,9 @@ cc_library(
         ":logger",
         ":mem",
         ":tox",
+        ":tox_attributes",
         ":tox_pack",
+        ":tox_system",
         ":tox_unpack",
         "//c-toxcore/third_party:cmp",
     ],
@@ -1180,8 +1437,10 @@ cc_test(
     srcs = ["tox_events_test.cc"],
     deps = [
         ":crypto_core",
+        ":os_system",
         ":tox",
         ":tox_events",
+        ":tox_system",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
diff --git a/toxcore/DHT_test.cc b/toxcore/DHT_test.cc
index f817efe9217..552bf4f7bfb 100644
--- a/toxcore/DHT_test.cc
+++ b/toxcore/DHT_test.cc
@@ -12,6 +12,7 @@
 #include "crypto_core.h"
 #include "crypto_core_test_util.hh"
 #include "logger.h"
+#include "mem.h"
 #include "mem_test_util.hh"
 #include "mono_time.h"
 #include "network.h"
@@ -337,7 +338,7 @@ TEST(AnnounceNodes, SetAndTest)
 
     Logger *log = logger_new(mem);
     ASSERT_NE(log, nullptr);
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     ASSERT_NE(mono_time, nullptr);
     Ptr<Networking_Core> net(new_networking_no_udp(log, mem, ns));
     ASSERT_NE(net, nullptr);
diff --git a/toxcore/LAN_discovery.c b/toxcore/LAN_discovery.c
index cadcf04e90d..005a6278149 100644
--- a/toxcore/LAN_discovery.c
+++ b/toxcore/LAN_discovery.c
@@ -41,6 +41,7 @@
 #include "crypto_core.h"
 #include "mem.h"
 #include "network.h"
+#include "tox_network.h"
 
 #define MAX_INTERFACES 16
 
diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc
index 2ee4a72f258..b4ec1d6dab4 100644
--- a/toxcore/Makefile.inc
+++ b/toxcore/Makefile.inc
@@ -1,12 +1,24 @@
 lib_LTLIBRARIES += libtoxcore.la
 
 libtoxcore_la_include_HEADERS = \
-                        ../toxcore/tox.h
+                        ../toxcore/tox.h \
+                        ../toxcore/tox_attributes.h \
+                        ../toxcore/tox_dispatch.h \
+                        ../toxcore/tox_events.h \
+                        ../toxcore/tox_log.h \
+                        ../toxcore/tox_memory.h \
+                        ../toxcore/tox_network.h \
+                        ../toxcore/tox_options.h \
+                        ../toxcore/tox_system.h \
+                        ../toxcore/tox_system_impl.h \
+                        ../toxcore/tox_time.h
 
-libtoxcore_la_includedir = $(includedir)/tox
+libtoxcore_la_includedir = $(includedir)/tox/toxcore
 
 libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \
                         ../third_party/cmp/cmp.h \
+                        ../toxcore/announce.c \
+                        ../toxcore/announce.h \
                         ../toxcore/attributes.h \
                         ../toxcore/bin_pack.c \
                         ../toxcore/bin_pack.h \
@@ -14,6 +26,12 @@ libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \
                         ../toxcore/bin_unpack.h \
                         ../toxcore/ccompat.c \
                         ../toxcore/ccompat.h \
+                        ../toxcore/crypto_core.c \
+                        ../toxcore/crypto_core.h \
+                        ../toxcore/crypto_core_pack.c \
+                        ../toxcore/crypto_core_pack.h \
+                        ../toxcore/DHT.c \
+                        ../toxcore/DHT.h \
                         ../toxcore/events/conference_connected.c \
                         ../toxcore/events/conference_invite.c \
                         ../toxcore/events/conference_message.c \
@@ -37,7 +55,6 @@ libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \
                         ../toxcore/events/friend_status.c \
                         ../toxcore/events/friend_status_message.c \
                         ../toxcore/events/friend_typing.c \
-                        ../toxcore/events/self_connection_status.c \
                         ../toxcore/events/group_custom_packet.c \
                         ../toxcore/events/group_custom_private_packet.c \
                         ../toxcore/events/group_invite.c \
@@ -56,97 +73,126 @@ libtoxcore_la_SOURCES = ../third_party/cmp/cmp.c \
                         ../toxcore/events/group_topic.c \
                         ../toxcore/events/group_topic_lock.c \
                         ../toxcore/events/group_voice_state.c \
-                        ../toxcore/DHT.h \
-                        ../toxcore/DHT.c \
-                        ../toxcore/mem.h \
+                        ../toxcore/events/self_connection_status.c \
+                        ../toxcore/forwarding.c \
+                        ../toxcore/forwarding.h \
+                        ../toxcore/friend_connection.c \
+                        ../toxcore/friend_connection.h \
+                        ../toxcore/friend_requests.c \
+                        ../toxcore/friend_requests.h \
+                        ../toxcore/group_announce.c \
+                        ../toxcore/group_announce.h \
+                        ../toxcore/group.c \
+                        ../toxcore/group_chats.c \
+                        ../toxcore/group_chats.h \
+                        ../toxcore/group_common.h \
+                        ../toxcore/group_connection.c \
+                        ../toxcore/group_connection.h \
+                        ../toxcore/group.h \
+                        ../toxcore/group_moderation.c \
+                        ../toxcore/group_moderation.h \
+                        ../toxcore/group_onion_announce.c \
+                        ../toxcore/group_onion_announce.h \
+                        ../toxcore/group_pack.c \
+                        ../toxcore/group_pack.h \
+                        ../toxcore/LAN_discovery.c \
+                        ../toxcore/LAN_discovery.h \
+                        ../toxcore/list.c \
+                        ../toxcore/list.h \
+                        ../toxcore/logger.c \
+                        ../toxcore/logger.h \
                         ../toxcore/mem.c \
-                        ../toxcore/mono_time.h \
+                        ../toxcore/mem.h \
+                        ../toxcore/Messenger.c \
+                        ../toxcore/Messenger.h \
                         ../toxcore/mono_time.c \
-                        ../toxcore/network.h \
-                        ../toxcore/network.c \
-                        ../toxcore/crypto_core.h \
-                        ../toxcore/crypto_core.c \
-                        ../toxcore/crypto_core_pack.h \
-                        ../toxcore/crypto_core_pack.c \
-                        ../toxcore/timed_auth.h \
-                        ../toxcore/timed_auth.c \
-                        ../toxcore/ping_array.h \
-                        ../toxcore/ping_array.c \
-                        ../toxcore/net_crypto.h \
+                        ../toxcore/mono_time.h \
                         ../toxcore/net_crypto.c \
+                        ../toxcore/net_crypto.h \
                         ../toxcore/net_profile.c \
                         ../toxcore/net_profile.h \
-                        ../toxcore/friend_requests.h \
-                        ../toxcore/friend_requests.c \
-                        ../toxcore/LAN_discovery.h \
-                        ../toxcore/LAN_discovery.c \
-                        ../toxcore/friend_connection.h \
-                        ../toxcore/friend_connection.c \
-                        ../toxcore/Messenger.h \
-                        ../toxcore/Messenger.c \
-                        ../toxcore/ping.h \
+                        ../toxcore/network.c \
+                        ../toxcore/network.h \
+                        ../toxcore/onion_announce.c \
+                        ../toxcore/onion_announce.h \
+                        ../toxcore/onion.c \
+                        ../toxcore/onion_client.c \
+                        ../toxcore/onion_client.h \
+                        ../toxcore/onion.h \
+                        ../toxcore/os_log.c \
+                        ../toxcore/os_log.h \
+                        ../toxcore/os_memory.c \
+                        ../toxcore/os_memory.h \
+                        ../toxcore/os_network.c \
+                        ../toxcore/os_network.h \
+                        ../toxcore/os_network_impl.h \
+                        ../toxcore/os_random.c \
+                        ../toxcore/os_random.h \
+                        ../toxcore/os_system.c \
+                        ../toxcore/os_system.h \
+                        ../toxcore/os_time.c \
+                        ../toxcore/os_time.h \
+                        ../toxcore/ping_array.c \
+                        ../toxcore/ping_array.h \
                         ../toxcore/ping.c \
-                        ../toxcore/shared_key_cache.h \
+                        ../toxcore/ping.h \
                         ../toxcore/shared_key_cache.c \
+                        ../toxcore/shared_key_cache.h \
                         ../toxcore/sort.h \
                         ../toxcore/sort.c \
-                        ../toxcore/state.h \
                         ../toxcore/state.c \
-                        ../toxcore/tox.h \
+                        ../toxcore/state.h \
+                        ../toxcore/TCP_client.c \
+                        ../toxcore/TCP_client.h \
+                        ../toxcore/TCP_common.c \
+                        ../toxcore/TCP_common.h \
+                        ../toxcore/TCP_connection.c \
+                        ../toxcore/TCP_connection.h \
+                        ../toxcore/TCP_server.c \
+                        ../toxcore/TCP_server.h \
+                        ../toxcore/timed_auth.c \
+                        ../toxcore/timed_auth.h \
+                        ../toxcore/tox_api.c \
+                        ../toxcore/tox_attributes.h \
                         ../toxcore/tox.c \
-                        ../toxcore/tox_dispatch.h \
                         ../toxcore/tox_dispatch.c \
-                        ../toxcore/tox_event.h \
+                        ../toxcore/tox_dispatch.h \
                         ../toxcore/tox_event.c \
-                        ../toxcore/tox_events.h \
+                        ../toxcore/tox_event.h \
                         ../toxcore/tox_events.c \
-                        ../toxcore/tox_pack.h \
+                        ../toxcore/tox_events.h \
+                        ../toxcore/tox.h \
+                        ../toxcore/tox_impl.h \
+                        ../toxcore/tox_log.c \
+                        ../toxcore/tox_log.h \
+                        ../toxcore/tox_log_impl.h \
+                        ../toxcore/tox_log_level.c \
+                        ../toxcore/tox_log_level.h \
+                        ../toxcore/tox_memory.c \
+                        ../toxcore/tox_memory.h \
+                        ../toxcore/tox_memory_impl.h \
+                        ../toxcore/tox_network.c \
+                        ../toxcore/tox_network.h \
+                        ../toxcore/tox_network_impl.h \
+                        ../toxcore/tox_options.c \
+                        ../toxcore/tox_options.h \
                         ../toxcore/tox_pack.c \
-                        ../toxcore/tox_unpack.h \
-                        ../toxcore/tox_unpack.c \
+                        ../toxcore/tox_pack.h \
                         ../toxcore/tox_private.c \
                         ../toxcore/tox_private.h \
-                        ../toxcore/tox_struct.h \
-                        ../toxcore/tox_api.c \
-                        ../toxcore/util.h \
+                        ../toxcore/tox_random.c \
+                        ../toxcore/tox_random.h \
+                        ../toxcore/tox_random_impl.h \
+                        ../toxcore/tox_system.c \
+                        ../toxcore/tox_system.h \
+                        ../toxcore/tox_system_impl.h \
+                        ../toxcore/tox_time.c \
+                        ../toxcore/tox_time.h \
+                        ../toxcore/tox_time_impl.h \
+                        ../toxcore/tox_unpack.c \
+                        ../toxcore/tox_unpack.h \
                         ../toxcore/util.c \
-                        ../toxcore/group.h \
-                        ../toxcore/group.c \
-                        ../toxcore/group_announce.h \
-                        ../toxcore/group_announce.c \
-                        ../toxcore/group_onion_announce.c \
-                        ../toxcore/group_onion_announce.h \
-                        ../toxcore/group_chats.h \
-                        ../toxcore/group_chats.c \
-                        ../toxcore/group_common.h \
-                        ../toxcore/group_connection.c \
-                        ../toxcore/group_connection.h \
-                        ../toxcore/group_pack.c \
-                        ../toxcore/group_pack.h \
-                        ../toxcore/group_moderation.c \
-                        ../toxcore/group_moderation.h \
-                        ../toxcore/onion.h \
-                        ../toxcore/onion.c \
-                        ../toxcore/logger.h \
-                        ../toxcore/logger.c \
-                        ../toxcore/onion_announce.h \
-                        ../toxcore/onion_announce.c \
-                        ../toxcore/onion_client.h \
-                        ../toxcore/onion_client.c \
-                        ../toxcore/announce.h \
-                        ../toxcore/announce.c \
-                        ../toxcore/forwarding.h \
-                        ../toxcore/forwarding.c \
-                        ../toxcore/TCP_client.h \
-                        ../toxcore/TCP_client.c \
-                        ../toxcore/TCP_common.h \
-                        ../toxcore/TCP_common.c \
-                        ../toxcore/TCP_server.h \
-                        ../toxcore/TCP_server.c \
-                        ../toxcore/TCP_connection.h \
-                        ../toxcore/TCP_connection.c \
-                        ../toxcore/list.c \
-                        ../toxcore/list.h
+                        ../toxcore/util.h
 
 libtoxcore_la_CFLAGS =  -I$(top_srcdir) \
                         -I$(top_srcdir)/toxcore \
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c
index e7f8ee9cee8..826ad8d3589 100644
--- a/toxcore/Messenger.c
+++ b/toxcore/Messenger.c
@@ -3171,7 +3171,7 @@ static bool handle_groups_load(void *obj, Bin_Unpack *bu)
 non_null()
 static State_Load_Status groups_load(Messenger *m, const uint8_t *data, uint32_t length)
 {
-    if (!bin_unpack_obj(handle_groups_load, m, data, length)) {
+    if (!bin_unpack_obj(handle_groups_load, m, data, length, m->mem)) {
         LOGGER_ERROR(m->log, "msgpack failed to unpack groupchats array");
         return STATE_LOAD_STATUS_ERROR;
     }
diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c
index 1982412cdac..177277bd67a 100644
--- a/toxcore/TCP_client.c
+++ b/toxcore/TCP_client.c
@@ -22,6 +22,7 @@
 #include "mono_time.h"
 #include "net_profile.h"
 #include "network.h"
+#include "tox_network.h"
 #include "util.h"
 
 typedef struct TCP_Client_Conn {
diff --git a/toxcore/TCP_common.c b/toxcore/TCP_common.c
index e9f67dcffb4..6dc316da6ad 100644
--- a/toxcore/TCP_common.c
+++ b/toxcore/TCP_common.c
@@ -13,6 +13,7 @@
 #include "logger.h"
 #include "mem.h"
 #include "network.h"
+#include "tox_network.h"
 
 void wipe_priority_list(const Memory *mem, TCP_Priority_List *p)
 {
diff --git a/toxcore/TCP_common.h b/toxcore/TCP_common.h
index dd07fd1ec5d..6b911dcf073 100644
--- a/toxcore/TCP_common.h
+++ b/toxcore/TCP_common.h
@@ -12,6 +12,7 @@
 #include "mem.h"
 #include "net_profile.h"
 #include "network.h"
+#include "tox_network.h"
 
 typedef struct TCP_Priority_List TCP_Priority_List;
 struct TCP_Priority_List {
diff --git a/toxcore/TCP_connection.h b/toxcore/TCP_connection.h
index 33388d5f8b5..f7d48f98aab 100644
--- a/toxcore/TCP_connection.h
+++ b/toxcore/TCP_connection.h
@@ -15,7 +15,6 @@
 #include "DHT.h"  // for Node_format
 #include "TCP_client.h"
 #include "TCP_common.h"
-#include "attributes.h"
 #include "crypto_core.h"
 #include "forwarding.h"
 #include "logger.h"
@@ -23,6 +22,7 @@
 #include "mono_time.h"
 #include "net_profile.h"
 #include "network.h"
+#include "attributes.h"
 
 #define TCP_CONN_NONE 0
 #define TCP_CONN_VALID 1
diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c
index f5b32d9c5f2..2b7a529b394 100644
--- a/toxcore/TCP_server.c
+++ b/toxcore/TCP_server.c
@@ -30,6 +30,7 @@
 #include "net_profile.h"
 #include "network.h"
 #include "onion.h"
+#include "tox_network.h"
 
 #ifdef TCP_SERVER_USE_EPOLL
 #define TCP_SOCKET_LISTENING 0
@@ -920,7 +921,7 @@ static int accept_connection(TCP_Server *tcp_server, Socket sock)
 }
 
 non_null()
-static Socket new_listening_tcp_socket(const Logger *logger, const Network *ns, Family family, uint16_t port)
+static Socket new_listening_tcp_socket(const Logger *logger, const Network *ns, const Memory *mem, Family family, uint16_t port)
 {
     const Socket sock = net_socket(ns, family, TOX_SOCK_STREAM, TOX_PROTO_TCP);
 
@@ -939,7 +940,7 @@ static Socket new_listening_tcp_socket(const Logger *logger, const Network *ns,
         ok = set_socket_reuseaddr(ns, sock);
     }
 
-    ok = ok && bind_to_port(ns, sock, family, port) && (net_listen(ns, sock, TCP_MAX_BACKLOG) == 0);
+    ok = ok && bind_to_port(ns, mem, sock, family, port) && (net_listen(ns, sock, TCP_MAX_BACKLOG) == 0);
 
     if (!ok) {
         char *const error = net_new_strerror(net_error());
@@ -1015,7 +1016,7 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random
     const Family family = ipv6_enabled ? net_family_ipv6() : net_family_ipv4();
 
     for (uint32_t i = 0; i < num_sockets; ++i) {
-        const Socket sock = new_listening_tcp_socket(logger, ns, family, ports[i]);
+        const Socket sock = new_listening_tcp_socket(logger, ns, mem, family, ports[i]);
 
         if (!sock_valid(sock)) {
             continue;
diff --git a/toxcore/announce.c b/toxcore/announce.c
index dc34ca05e31..be14db57fc4 100644
--- a/toxcore/announce.c
+++ b/toxcore/announce.c
@@ -552,11 +552,12 @@ static int create_reply_plain(Announcements *announce,
     }
 }
 
-non_null(1, 2, 5, 7) nullable(3)
+non_null(1, 2, 5, 7, 9) nullable(3)
 static int create_reply(Announcements *announce, const IP_Port *source,
                         const uint8_t *sendback, uint16_t sendback_length,
                         const uint8_t *data, uint16_t length,
-                        uint8_t *reply, uint16_t reply_max_length)
+                        uint8_t *reply, uint16_t reply_max_length,
+                        const Memory *mem)
 {
     const int plain_len = (int)length - (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE);
 
@@ -616,7 +617,8 @@ static void forwarded_request_callback(void *object, const IP_Port *forwarder,
 
     const int len = create_reply(announce, forwarder,
                                  sendback, sendback_length,
-                                 data, length, reply, sizeof(reply));
+                                 data, length, reply, sizeof(reply),
+                                 announce->mem);
 
     if (len == -1) {
         return;
@@ -633,8 +635,8 @@ static int handle_dht_announce_request(
 
     uint8_t reply[MAX_FORWARD_DATA_SIZE];
 
-    const int len
-        = create_reply(announce, source, nullptr, 0, packet, length, reply, sizeof(reply));
+    const int len = create_reply(
+                        announce, source, nullptr, 0, packet, length, reply, sizeof(reply), announce->mem);
 
     if (len == -1) {
         return -1;
@@ -668,7 +670,7 @@ Announcements *new_announcements(const Logger *log, const Memory *mem, const Ran
     new_hmac_key(announce->rng, announce->hmac_key);
     announce->shared_keys = shared_key_cache_new(log, mono_time, mem, announce->secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT);
     if (announce->shared_keys == nullptr) {
-        mem_delete(announce->mem, announce);
+        mem_delete(mem, announce);
         return nullptr;
     }
 
diff --git a/toxcore/bin_pack.c b/toxcore/bin_pack.c
index 82e9d20dd41..f9f83618b97 100644
--- a/toxcore/bin_pack.c
+++ b/toxcore/bin_pack.c
@@ -11,8 +11,11 @@
 #include "attributes.h"
 #include "ccompat.h"
 #include "logger.h"
+#include "mem.h"
 
 struct Bin_Pack {
+    const Memory *mem;
+
     uint8_t *bytes;
     uint32_t bytes_size;
     uint32_t bytes_pos;
@@ -58,6 +61,7 @@ static size_t buf_writer(cmp_ctx_t *ctx, const void *data, size_t count)
 non_null(1) nullable(2)
 static void bin_pack_init(Bin_Pack *bp, uint8_t *buf, uint32_t buf_size)
 {
+    bp->mem = nullptr;
     bp->bytes = buf;
     bp->bytes_size = buf_size;
     bp->bytes_pos = 0;
diff --git a/toxcore/bin_pack.h b/toxcore/bin_pack.h
index 4b2927f9721..480af9e4f34 100644
--- a/toxcore/bin_pack.h
+++ b/toxcore/bin_pack.h
@@ -9,6 +9,7 @@
 
 #include "attributes.h"
 #include "logger.h"
+#include "mem.h"
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/toxcore/bin_pack_test.cc b/toxcore/bin_pack_test.cc
index 08e37ba549b..42ee51c49b9 100644
--- a/toxcore/bin_pack_test.cc
+++ b/toxcore/bin_pack_test.cc
@@ -8,6 +8,7 @@
 
 #include "bin_unpack.h"
 #include "logger.h"
+#include "mem_test_util.hh"
 
 namespace {
 
@@ -24,6 +25,8 @@ TEST(BinPack, TooSmallBufferIsNotExceeded)
 
 TEST(BinPack, PackedUint64CanBeUnpacked)
 {
+    Test_Memory mem;
+
     const uint64_t orig = 1234567812345678LL;
     std::array<uint8_t, 8> buf;
     EXPECT_TRUE(bin_pack_obj(
@@ -37,12 +40,14 @@ TEST(BinPack, PackedUint64CanBeUnpacked)
         [](void *obj, Bin_Unpack *bu) {
             return bin_unpack_u64_b(bu, static_cast<uint64_t *>(obj));
         },
-        &unpacked, buf.data(), buf.size()));
+        &unpacked, buf.data(), buf.size(), mem));
     EXPECT_EQ(unpacked, 1234567812345678LL);
 }
 
 TEST(BinPack, MsgPackedUint8CanBeUnpackedAsUint32)
 {
+    Test_Memory mem;
+
     const uint8_t orig = 123;
     std::array<uint8_t, 2> buf;
     EXPECT_TRUE(bin_pack_obj(
@@ -54,12 +59,14 @@ TEST(BinPack, MsgPackedUint8CanBeUnpackedAsUint32)
     uint32_t unpacked = 0;
     EXPECT_TRUE(bin_unpack_obj(
         [](void *obj, Bin_Unpack *bu) { return bin_unpack_u32(bu, static_cast<uint32_t *>(obj)); },
-        &unpacked, buf.data(), buf.size()));
+        &unpacked, buf.data(), buf.size(), mem));
     EXPECT_EQ(unpacked, 123);
 }
 
 TEST(BinPack, MsgPackedUint32CanBeUnpackedAsUint8IfSmallEnough)
 {
+    Test_Memory mem;
+
     const uint32_t orig = 123;
     std::array<uint8_t, 2> buf;
     EXPECT_TRUE(bin_pack_obj(
@@ -71,13 +78,15 @@ TEST(BinPack, MsgPackedUint32CanBeUnpackedAsUint8IfSmallEnough)
     uint8_t unpacked = 0;
     EXPECT_TRUE(bin_unpack_obj(
         [](void *obj, Bin_Unpack *bu) { return bin_unpack_u08(bu, static_cast<uint8_t *>(obj)); },
-        &unpacked, buf.data(), buf.size()));
+        &unpacked, buf.data(), buf.size(), mem));
 
     EXPECT_EQ(unpacked, 123);
 }
 
 TEST(BinPack, LargeMsgPackedUint32CannotBeUnpackedAsUint8)
 {
+    Test_Memory mem;
+
     const uint32_t orig = 1234567;
     std::array<uint8_t, 5> buf;
     EXPECT_TRUE(bin_pack_obj(
@@ -89,11 +98,13 @@ TEST(BinPack, LargeMsgPackedUint32CannotBeUnpackedAsUint8)
     uint8_t unpacked = 0;
     EXPECT_FALSE(bin_unpack_obj(
         [](void *obj, Bin_Unpack *bu) { return bin_unpack_u08(bu, static_cast<uint8_t *>(obj)); },
-        &unpacked, buf.data(), buf.size()));
+        &unpacked, buf.data(), buf.size(), mem));
 }
 
 TEST(BinPack, BinCanHoldPackedInts)
 {
+    Test_Memory mem;
+
     struct Stuff {
         uint64_t u64;
         uint16_t u16;
@@ -121,13 +132,15 @@ TEST(BinPack, BinCanHoldPackedInts)
                 && bin_unpack_u64_b(bu, &stuff->u64)  //
                 && bin_unpack_u16_b(bu, &stuff->u16);
         },
-        &unpacked, buf.data(), buf.size()));
+        &unpacked, buf.data(), buf.size(), mem));
     EXPECT_EQ(unpacked.u64, 1234567812345678LL);
     EXPECT_EQ(unpacked.u16, 54321);
 }
 
 TEST(BinPack, BinCanHoldArbitraryData)
 {
+    Test_Memory mem;
+
     std::array<uint8_t, 7> buf;
     EXPECT_TRUE(bin_pack_obj(
         [](const void *obj, const Logger *logger, Bin_Pack *bp) {
@@ -142,12 +155,14 @@ TEST(BinPack, BinCanHoldArbitraryData)
             uint8_t *data = static_cast<uint8_t *>(obj);
             return bin_unpack_bin_fixed(bu, data, 5);
         },
-        str.data(), buf.data(), buf.size()));
+        str.data(), buf.data(), buf.size(), mem));
     EXPECT_EQ(str, (std::array<uint8_t, 5>{'h', 'e', 'l', 'l', 'o'}));
 }
 
 TEST(BinPack, OversizedArrayFailsUnpack)
 {
+    Test_Memory mem;
+
     std::array<uint8_t, 1> buf = {0x91};
 
     uint32_t size;
@@ -156,7 +171,7 @@ TEST(BinPack, OversizedArrayFailsUnpack)
             uint32_t *size_ptr = static_cast<uint32_t *>(obj);
             return bin_unpack_array(bu, size_ptr);
         },
-        &size, buf.data(), buf.size()));
+        &size, buf.data(), buf.size(), mem));
 }
 
 }  // namespace
diff --git a/toxcore/bin_unpack.c b/toxcore/bin_unpack.c
index 82b7f8d9d56..27c07cb7d44 100644
--- a/toxcore/bin_unpack.c
+++ b/toxcore/bin_unpack.c
@@ -5,14 +5,16 @@
 #include "bin_unpack.h"
 
 #include <assert.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "../third_party/cmp/cmp.h"
 #include "attributes.h"
 #include "ccompat.h"
+#include "mem.h"
 
 struct Bin_Unpack {
+    const Memory *mem;
+
     const uint8_t *bytes;
     uint32_t bytes_size;
     cmp_ctx_t ctx;
@@ -54,17 +56,18 @@ static size_t null_writer(cmp_ctx_t *ctx, const void *data, size_t count)
 }
 
 non_null()
-static void bin_unpack_init(Bin_Unpack *bu, const uint8_t *buf, uint32_t buf_size)
+static void bin_unpack_init(Bin_Unpack *bu, const uint8_t *buf, uint32_t buf_size, const Memory *mem)
 {
+    bu->mem = mem;
     bu->bytes = buf;
     bu->bytes_size = buf_size;
     cmp_init(&bu->ctx, bu, buf_reader, buf_skipper, null_writer);
 }
 
-bool bin_unpack_obj(bin_unpack_cb *callback, void *obj, const uint8_t *buf, uint32_t buf_size)
+bool bin_unpack_obj(bin_unpack_cb *callback, void *obj, const uint8_t *buf, uint32_t buf_size, const Memory *mem)
 {
     Bin_Unpack bu;
-    bin_unpack_init(&bu, buf, buf_size);
+    bin_unpack_init(&bu, buf, buf_size, mem);
     return callback(obj, &bu);
 }
 
@@ -120,10 +123,14 @@ bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_pt
         // There aren't as many bytes as this bin claims to want to allocate.
         return false;
     }
-    uint8_t *const data = (uint8_t *)malloc(bin_size);
+    uint8_t *const data = (uint8_t *)mem_balloc(bu->mem, bin_size);
+
+    if (data == nullptr) {
+        return false;
+    }
 
     if (!bin_unpack_bin_b(bu, data, bin_size)) {
-        free(data);
+        mem_delete(bu->mem, data);
         return false;
     }
 
diff --git a/toxcore/bin_unpack.h b/toxcore/bin_unpack.h
index 35574769a24..05114537f09 100644
--- a/toxcore/bin_unpack.h
+++ b/toxcore/bin_unpack.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include "attributes.h"
+#include "mem.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -46,7 +47,7 @@ typedef bool bin_unpack_cb(void *obj, Bin_Unpack *bu);
  * @retval false if an error occurred (e.g. buffer overrun).
  */
 non_null()
-bool bin_unpack_obj(bin_unpack_cb *callback, void *obj, const uint8_t *buf, uint32_t buf_size);
+bool bin_unpack_obj(bin_unpack_cb *callback, void *obj, const uint8_t *buf, uint32_t buf_size, const Memory *mem);
 
 /** @brief Start unpacking a MessagePack array.
  *
diff --git a/toxcore/crypto_core.c b/toxcore/crypto_core.c
index 8a6aa38fbab..10c94d23958 100644
--- a/toxcore/crypto_core.c
+++ b/toxcore/crypto_core.c
@@ -13,6 +13,7 @@
 #include "attributes.h"
 #include "ccompat.h"
 #include "mem.h"
+#include "tox_random.h"
 #include "util.h"
 
 static_assert(CRYPTO_PUBLIC_KEY_SIZE == crypto_box_PUBLICKEYBYTES,
@@ -206,7 +207,7 @@ uint64_t random_u64(const Random *rng)
 
 uint32_t random_range_u32(const Random *rng, uint32_t upper_bound)
 {
-    return rng->funcs->random_uniform(rng->obj, upper_bound);
+    return tox_random_uniform(rng, upper_bound);
 }
 
 bool crypto_signature_create(uint8_t signature[CRYPTO_SIGNATURE_SIZE],
@@ -495,41 +496,7 @@ void crypto_sha512(uint8_t hash[CRYPTO_SHA512_SIZE], const uint8_t *data, size_t
 #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
 }
 
-non_null()
-static void sys_random_bytes(void *obj, uint8_t *bytes, size_t length)
-{
-    randombytes(bytes, length);
-}
-
-non_null()
-static uint32_t sys_random_uniform(void *obj, uint32_t upper_bound)
-{
-    return randombytes_uniform(upper_bound);
-}
-
-static const Random_Funcs os_random_funcs = {
-    sys_random_bytes,
-    sys_random_uniform,
-};
-
-static const Random os_random_obj = {&os_random_funcs};
-
-const Random *os_random(void)
-{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-    if ((true)) {
-        return nullptr;
-    }
-#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
-    // It is safe to call this function more than once and from different
-    // threads -- subsequent calls won't have any effects.
-    if (sodium_init() == -1) {
-        return nullptr;
-    }
-    return &os_random_obj;
-}
-
 void random_bytes(const Random *rng, uint8_t *bytes, size_t length)
 {
-    rng->funcs->random_bytes(rng->obj, bytes, length);
+    tox_random_bytes(rng, bytes, length);
 }
diff --git a/toxcore/crypto_core.h b/toxcore/crypto_core.h
index 558118e397f..14338b003b0 100644
--- a/toxcore/crypto_core.h
+++ b/toxcore/crypto_core.h
@@ -17,6 +17,7 @@
 
 #include "attributes.h"
 #include "mem.h"
+#include "tox_random.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -78,44 +79,6 @@ extern "C" {
  */
 #define CRYPTO_SHA512_SIZE             64
 
-/** @brief Fill a byte array with random bytes.
- *
- * This is the key generator callback and as such must be a cryptographically
- * secure pseudo-random number generator (CSPRNG). The security of Tox heavily
- * depends on the security of this RNG.
- */
-typedef void crypto_random_bytes_cb(void *obj, uint8_t *bytes, size_t length);
-
-/** @brief Generate a random integer between 0 and @p upper_bound.
- *
- * Should produce a uniform random distribution, but Tox security does not
- * depend on this being correct. In principle, it could even be a non-CSPRNG.
- */
-typedef uint32_t crypto_random_uniform_cb(void *obj, uint32_t upper_bound);
-
-/** @brief Virtual function table for Random. */
-typedef struct Random_Funcs {
-    crypto_random_bytes_cb *random_bytes;
-    crypto_random_uniform_cb *random_uniform;
-} Random_Funcs;
-
-/** @brief Random number generator object.
- *
- * Can be used by test code and fuzzers to make toxcore behave in specific
- * well-defined (non-random) ways. Production code ought to use libsodium's
- * CSPRNG and use `os_random` below.
- */
-typedef struct Random {
-    const Random_Funcs *funcs;
-    void *obj;
-} Random;
-
-/** @brief System random number generator.
- *
- * Uses libsodium's CSPRNG (on Linux, `/dev/urandom`).
- */
-const Random *os_random(void);
-
 /**
  * @brief The number of bytes in an encryption public key used by DHT group chats.
  */
@@ -238,6 +201,11 @@ bool crypto_sha512_eq(const uint8_t cksum1[CRYPTO_SHA512_SIZE], const uint8_t ck
 non_null()
 bool crypto_sha256_eq(const uint8_t cksum1[CRYPTO_SHA256_SIZE], const uint8_t cksum2[CRYPTO_SHA256_SIZE]);
 
+/**
+ * @brief Shorter internal name for the RNG type.
+ */
+typedef Tox_Random Random;
+
 /**
  * @brief Return a random 8 bit integer.
  */
diff --git a/toxcore/crypto_core_test_util.cc b/toxcore/crypto_core_test_util.cc
index b48871a5381..19d8ff94921 100644
--- a/toxcore/crypto_core_test_util.cc
+++ b/toxcore/crypto_core_test_util.cc
@@ -5,15 +5,16 @@
 
 #include "crypto_core.h"
 #include "test_util.hh"
+#include "tox_random_impl.h"
 
-Random_Funcs const Random_Class::vtable = {
-    Method<crypto_random_bytes_cb, Random_Class>::invoke<&Random_Class::random_bytes>,
-    Method<crypto_random_uniform_cb, Random_Class>::invoke<&Random_Class::random_uniform>,
+Tox_Random_Funcs const Random_Class::vtable = {
+    Method<tox_random_bytes_cb, Random_Class>::invoke<&Random_Class::random_bytes>,
+    Method<tox_random_uniform_cb, Random_Class>::invoke<&Random_Class::random_uniform>,
 };
 
 Random_Class::~Random_Class() = default;
 
-void Test_Random::random_bytes(void *obj, uint8_t *bytes, size_t length)
+void Test_Random::random_bytes(void *obj, uint8_t *bytes, uint32_t length)
 {
     std::generate(bytes, &bytes[length], std::ref(lcg));
 }
diff --git a/toxcore/crypto_core_test_util.hh b/toxcore/crypto_core_test_util.hh
index 91a9d68ee29..53b9b07d1bc 100644
--- a/toxcore/crypto_core_test_util.hh
+++ b/toxcore/crypto_core_test_util.hh
@@ -8,12 +8,13 @@
 
 #include "crypto_core.h"
 #include "test_util.hh"
+#include "tox_random_impl.h"
 
 struct Random_Class {
-    static Random_Funcs const vtable;
-    Random const self;
+    static Tox_Random_Funcs const vtable;
+    Tox_Random const self;
 
-    operator Random const *() const { return &self; }
+    operator Tox_Random const *() const { return &self; }
 
     Random_Class(Random_Class const &) = default;
     Random_Class()
@@ -22,8 +23,8 @@ struct Random_Class {
     }
 
     virtual ~Random_Class();
-    virtual crypto_random_bytes_cb random_bytes = 0;
-    virtual crypto_random_uniform_cb random_uniform = 0;
+    virtual tox_random_bytes_cb random_bytes = 0;
+    virtual tox_random_uniform_cb random_uniform = 0;
 };
 
 /**
@@ -35,7 +36,7 @@ struct Random_Class {
 class Test_Random : public Random_Class {
     std::minstd_rand lcg;
 
-    void random_bytes(void *obj, uint8_t *bytes, size_t length) override;
+    void random_bytes(void *obj, uint8_t *bytes, uint32_t length) override;
     uint32_t random_uniform(void *obj, uint32_t upper_bound) override;
 };
 
@@ -83,6 +84,6 @@ inline bool operator==(PublicKey::Base const &pk1, PublicKey const &pk2)
 
 std::ostream &operator<<(std::ostream &out, PublicKey const &pk);
 
-PublicKey random_pk(const Random *rng);
+PublicKey random_pk(const Tox_Random *rng);
 
 #endif  // C_TOXCORE_TOXCORE_CRYPTO_CORE_TEST_UTIL_H
diff --git a/toxcore/events/dht_get_nodes_response.c b/toxcore/events/dht_get_nodes_response.c
index 918b97d1b77..7e2fda4be75 100644
--- a/toxcore/events/dht_get_nodes_response.c
+++ b/toxcore/events/dht_get_nodes_response.c
@@ -16,6 +16,7 @@
 #include "../tox_event.h"
 #include "../tox_events.h"
 #include "../tox_private.h"
+#include "../tox_system_impl.h"
 
 /*****************************************************
  *
diff --git a/toxcore/events/friend_request.c b/toxcore/events/friend_request.c
index 631c236bac5..98e39a4edf3 100644
--- a/toxcore/events/friend_request.c
+++ b/toxcore/events/friend_request.c
@@ -16,6 +16,7 @@
 #include "../tox_event.h"
 #include "../tox_events.h"
 #include "../tox_private.h"
+#include "../tox_system_impl.h"
 
 /*****************************************************
  *
diff --git a/toxcore/forwarding_fuzz_test.cc b/toxcore/forwarding_fuzz_test.cc
index 4b8521ab079..77bbea853f6 100644
--- a/toxcore/forwarding_fuzz_test.cc
+++ b/toxcore/forwarding_fuzz_test.cc
@@ -7,6 +7,8 @@
 
 #include "../testing/fuzzing/fuzz_support.hh"
 #include "../testing/fuzzing/fuzz_tox.hh"
+#include "os_memory.h"
+#include "os_network.h"
 
 namespace {
 
diff --git a/toxcore/friend_connection.c b/toxcore/friend_connection.c
index 78c6d4acdc6..409998a7745 100644
--- a/toxcore/friend_connection.c
+++ b/toxcore/friend_connection.c
@@ -120,7 +120,7 @@ static bool friendconn_id_valid(const Friend_Connections *fr_c, int friendcon_id
 
 /** @brief Set the size of the friend connections list to num.
  *
- * @retval false if realloc fails.
+ * @retval false if mem_vrealloc fails.
  * @retval true if it succeeds.
  */
 non_null()
@@ -940,6 +940,7 @@ Friend_Connections *new_friend_connections(
     temp->mono_time = mono_time;
     temp->mem = mem;
     temp->logger = logger;
+    temp->mem = mem;
     temp->dht = onion_get_dht(onion_c);
     temp->net_crypto = onion_get_net_crypto(onion_c);
     temp->onion_c = onion_c;
diff --git a/toxcore/group.c b/toxcore/group.c
index dbc38764032..5342df23c07 100644
--- a/toxcore/group.c
+++ b/toxcore/group.c
@@ -702,7 +702,7 @@ static bool delete_frozen(const Memory *mem, Group_c *g, uint32_t frozen_index)
  * @return peer index if peer is in the conference.
  * @retval -1 otherwise, and on error.
  */
-non_null(1) nullable(4)
+non_null(1, 5) nullable(4)
 static int note_peer_active(Group_Chats *g_c, uint32_t groupnumber, uint16_t peer_number, void *userdata)
 {
     Group_c *g = get_group_c(g_c, groupnumber);
diff --git a/toxcore/group.h b/toxcore/group.h
index 706428fdda7..2f1831510fd 100644
--- a/toxcore/group.h
+++ b/toxcore/group.h
@@ -13,11 +13,11 @@
 #include <stdint.h>
 
 #include "Messenger.h"
-#include "attributes.h"
 #include "crypto_core.h"
 #include "mem.h"
 #include "mono_time.h"
 #include "state.h"
+#include "attributes.h"
 
 typedef enum Groupchat_Type {
     GROUPCHAT_TYPE_TEXT,
diff --git a/toxcore/group_announce.c b/toxcore/group_announce.c
index caa70193aa8..8f1a8a90faa 100644
--- a/toxcore/group_announce.c
+++ b/toxcore/group_announce.c
@@ -19,8 +19,8 @@
 /**
  * Removes `announces` from `gc_announces_list`.
  */
-non_null()
-static void remove_announces(GC_Announces_List *gc_announces_list, GC_Announces *announces)
+non_null() static void remove_announces(
+    GC_Announces_List *gc_announces_list, GC_Announces *announces)
 {
     if (announces == nullptr || gc_announces_list == nullptr) {
         return;
@@ -43,8 +43,8 @@ static void remove_announces(GC_Announces_List *gc_announces_list, GC_Announces
  * Returns the announce designated by `chat_id`.
  * Returns null if no announce is found.
  */
-non_null()
-static GC_Announces *get_announces_by_chat_id(const GC_Announces_List *gc_announces_list,  const uint8_t *chat_id)
+non_null() static GC_Announces *get_announces_by_chat_id(
+    const GC_Announces_List *gc_announces_list, const uint8_t *chat_id)
 {
     GC_Announces *announces = gc_announces_list->root_announces;
 
@@ -59,11 +59,11 @@ static GC_Announces *get_announces_by_chat_id(const GC_Announces_List *gc_announ
     return nullptr;
 }
 
-int gca_get_announces(const GC_Announces_List *gc_announces_list, GC_Announce *gc_announces, uint8_t max_nodes,
-                      const uint8_t *chat_id, const uint8_t *except_public_key)
+int gca_get_announces(const GC_Announces_List *gc_announces_list, GC_Announce *gc_announces,
+                      uint8_t max_nodes, const uint8_t *chat_id, const uint8_t *except_public_key)
 {
-    if (gc_announces == nullptr || gc_announces_list == nullptr || chat_id == nullptr || max_nodes == 0
-            || except_public_key == nullptr) {
+    if (gc_announces == nullptr || gc_announces_list == nullptr || chat_id == nullptr
+            || max_nodes == 0 || except_public_key == nullptr) {
         return -1;
     }
 
@@ -75,7 +75,9 @@ int gca_get_announces(const GC_Announces_List *gc_announces_list, GC_Announce *g
 
     uint16_t added_count = 0;
 
-    for (size_t i = 0; i < announces->index && i < GCA_MAX_SAVED_ANNOUNCES_PER_GC && added_count < max_nodes; ++i) {
+    for (size_t i = 0;
+            i < announces->index && i < GCA_MAX_SAVED_ANNOUNCES_PER_GC && added_count < max_nodes;
+            ++i) {
         const size_t index = i % GCA_MAX_SAVED_ANNOUNCES_PER_GC;
 
         if (memcmp(except_public_key, announces->peer_announces[index].base_announce.peer_public_key,
@@ -107,7 +109,8 @@ uint16_t gca_pack_announces_list_size(uint16_t count)
     return count * GCA_ANNOUNCE_MAX_SIZE;
 }
 
-int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announce)
+int gca_pack_announce(
+    const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announce)
 {
     if (length < GCA_ANNOUNCE_MAX_SIZE) {
         LOGGER_ERROR(log, "Invalid announce length: %u", length);
@@ -140,7 +143,8 @@ int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const G
     }
 
     if (announce->ip_port_is_set) {
-        const int ip_port_length = pack_ip_port(log, data + offset, length - offset, &announce->ip_port);
+        const int ip_port_length
+            = pack_ip_port(log, data + offset, length - offset, &announce->ip_port);
 
         if (ip_port_length == -1) {
             LOGGER_ERROR(log, "Failed to pack ip_port");
@@ -150,8 +154,8 @@ int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const G
         offset += ip_port_length;
     }
 
-    const int nodes_length = pack_nodes(log, data + offset, length - offset, announce->tcp_relays,
-                                        announce->tcp_relays_count);
+    const int nodes_length = pack_nodes(
+                                 log, data + offset, length - offset, announce->tcp_relays, announce->tcp_relays_count);
 
     if (nodes_length == -1) {
         LOGGER_ERROR(log, "Failed to pack TCP nodes");
@@ -167,8 +171,8 @@ int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const G
  * Returns the size of the unpacked data on success.
  * Returns -1 on failure.
  */
-non_null()
-static int gca_unpack_announce(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announce)
+non_null() static int gca_unpack_announce(
+    const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announce)
 {
     if (length < ENC_PUBLIC_KEY_SIZE + 2) {
         LOGGER_ERROR(log, "Invalid announce length: %u", length);
@@ -204,7 +208,8 @@ static int gca_unpack_announce(const Logger *log, const uint8_t *data, uint16_t
             return -1;
         }
 
-        const int ip_port_length = unpack_ip_port(&announce->ip_port, data + offset, length - offset, false);
+        const int ip_port_length
+            = unpack_ip_port(&announce->ip_port, data + offset, length - offset, false);
 
         if (ip_port_length == -1) {
             LOGGER_ERROR(log, "Failed to unpack ip_port");
@@ -215,8 +220,8 @@ static int gca_unpack_announce(const Logger *log, const uint8_t *data, uint16_t
     }
 
     uint16_t nodes_length;
-    const int nodes_count = unpack_nodes(announce->tcp_relays, announce->tcp_relays_count, &nodes_length,
-                                         data + offset, length - offset, true);
+    const int nodes_count = unpack_nodes(announce->tcp_relays, announce->tcp_relays_count,
+                                         &nodes_length, data + offset, length - offset, true);
 
     if (nodes_count != announce->tcp_relays_count) {
         LOGGER_ERROR(log, "Failed to unpack TCP nodes");
@@ -226,8 +231,8 @@ static int gca_unpack_announce(const Logger *log, const uint8_t *data, uint16_t
     return offset + nodes_length;
 }
 
-int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length,
-                             const GC_Public_Announce *public_announce)
+int gca_pack_public_announce(
+    const Logger *log, uint8_t *data, uint16_t length, const GC_Public_Announce *public_announce)
 {
     if (public_announce == nullptr || data == nullptr || length < CHAT_ID_SIZE) {
         return -1;
@@ -235,8 +240,8 @@ int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length,
 
     memcpy(data, public_announce->chat_public_key, CHAT_ID_SIZE);
 
-    const int packed_size = gca_pack_announce(log, data + CHAT_ID_SIZE, length - CHAT_ID_SIZE,
-                            &public_announce->base_announce);
+    const int packed_size = gca_pack_announce(
+                                log, data + CHAT_ID_SIZE, length - CHAT_ID_SIZE, &public_announce->base_announce);
 
     if (packed_size < 0) {
         LOGGER_ERROR(log, "Failed to pack public group announce");
@@ -246,8 +251,8 @@ int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length,
     return packed_size + CHAT_ID_SIZE;
 }
 
-int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t length,
-                               GC_Public_Announce *public_announce)
+int gca_unpack_public_announce(
+    const Logger *log, const uint8_t *data, uint16_t length, GC_Public_Announce *public_announce)
 {
     if (length < CHAT_ID_SIZE) {
         LOGGER_ERROR(log, "invalid public announce length: %u", length);
@@ -266,8 +271,8 @@ int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t
 
     memcpy(public_announce->chat_public_key, data, CHAT_ID_SIZE);
 
-    const int base_announce_size = gca_unpack_announce(log, data + ENC_PUBLIC_KEY_SIZE, length - ENC_PUBLIC_KEY_SIZE,
-                                   &public_announce->base_announce);
+    const int base_announce_size = gca_unpack_announce(log, data + ENC_PUBLIC_KEY_SIZE,
+                                   length - ENC_PUBLIC_KEY_SIZE, &public_announce->base_announce);
 
     if (base_announce_size == -1) {
         LOGGER_ERROR(log, "Failed to unpack group announce");
@@ -277,8 +282,8 @@ int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t
     return base_announce_size + CHAT_ID_SIZE;
 }
 
-int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announces,
-                            uint8_t announces_count, size_t *processed)
+int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length,
+                            const GC_Announce *announces, uint8_t announces_count, size_t *processed)
 {
     if (data == nullptr) {
         LOGGER_ERROR(log, "data is null");
@@ -293,7 +298,8 @@ int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, c
     uint16_t offset = 0;
 
     for (size_t i = 0; i < announces_count; ++i) {
-        const int packed_length = gca_pack_announce(log, data + offset, length - offset, &announces[i]);
+        const int packed_length
+            = gca_pack_announce(log, data + offset, length - offset, &announces[i]);
 
         if (packed_length < 0) {
             LOGGER_ERROR(log, "Failed to pack group announce");
@@ -310,8 +316,8 @@ int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, c
     return announces_count;
 }
 
-int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announces,
-                              uint8_t max_count)
+int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length,
+                              GC_Announce *announces, uint8_t max_count)
 {
     if (data == nullptr) {
         LOGGER_ERROR(log, "data is null");
@@ -327,7 +333,8 @@ int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t l
     int announces_count = 0;
 
     for (size_t i = 0; i < max_count && length > offset; ++i) {
-        const int unpacked_length = gca_unpack_announce(log, data + offset, length - offset, &announces[i]);
+        const int unpacked_length
+            = gca_unpack_announce(log, data + offset, length - offset, &announces[i]);
 
         if (unpacked_length == -1) {
             LOGGER_WARNING(log, "Failed to unpack group announce: %d %d", length, offset);
@@ -343,11 +350,10 @@ int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t l
 
 non_null()
 static GC_Announces *gca_new_announces(
-    const Memory *mem,
     GC_Announces_List *gc_announces_list,
     const GC_Public_Announce *public_announce)
 {
-    GC_Announces *announces = (GC_Announces *)mem_alloc(mem, sizeof(GC_Announces));
+    GC_Announces *announces = (GC_Announces *)mem_alloc(gc_announces_list->mem, sizeof(GC_Announces));
 
     if (announces == nullptr) {
         return nullptr;
@@ -374,11 +380,12 @@ GC_Peer_Announce *gca_add_announce(const Memory *mem, const Mono_Time *mono_time
         return nullptr;
     }
 
-    GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, public_announce->chat_public_key);
+    GC_Announces *announces
+        = get_announces_by_chat_id(gc_announces_list, public_announce->chat_public_key);
 
     // No entry for this chat_id exists so we create one
     if (announces == nullptr) {
-        announces = gca_new_announces(mem, gc_announces_list, public_announce);
+        announces = gca_new_announces(gc_announces_list, public_announce);
 
         if (announces == nullptr) {
             return nullptr;
@@ -453,7 +460,8 @@ void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list)
         return;
     }
 
-    if (!mono_time_is_timeout(mono_time, gc_announces_list->last_timeout_check, GCA_DO_GCA_TIMEOUT)) {
+    if (!mono_time_is_timeout(
+                mono_time, gc_announces_list->last_timeout_check, GCA_DO_GCA_TIMEOUT)) {
         return;
     }
 
@@ -462,7 +470,8 @@ void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list)
     GC_Announces *announces = gc_announces_list->root_announces;
 
     while (announces != nullptr) {
-        if (mono_time_is_timeout(mono_time, announces->last_announce_received_timestamp, GCA_ANNOUNCE_SAVE_TIMEOUT)) {
+        if (mono_time_is_timeout(mono_time, announces->last_announce_received_timestamp,
+                                 GCA_ANNOUNCE_SAVE_TIMEOUT)) {
             GC_Announces *to_delete = announces;
             announces = announces->next_announce;
             remove_announces(gc_announces_list, to_delete);
diff --git a/toxcore/group_announce_fuzz_test.cc b/toxcore/group_announce_fuzz_test.cc
index 154e486d76e..1b6b76a501c 100644
--- a/toxcore/group_announce_fuzz_test.cc
+++ b/toxcore/group_announce_fuzz_test.cc
@@ -7,6 +7,7 @@
 
 #include "../testing/fuzzing/fuzz_support.hh"
 #include "mem_test_util.hh"
+#include "tox_time_impl.h"
 
 namespace {
 
@@ -53,12 +54,15 @@ void TestDoGca(Fuzz_Data &input)
 {
     Test_Memory mem;
     std::unique_ptr<Logger, void (*)(Logger *)> logger(logger_new(mem), logger_kill);
+    constexpr Tox_Time_Funcs mock_time_funcs = {
+        [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
+    };
 
     uint64_t clock = 1;
+    std::unique_ptr<Tox_Time, void (*)(Tox_Time *)> tm(
+        tox_time_new(&mock_time_funcs, &clock, mem), tox_time_free);
     std::unique_ptr<Mono_Time, std::function<void(Mono_Time *)>> mono_time(
-        mono_time_new(
-            mem, [](void *user_data) { return *static_cast<uint64_t *>(user_data); }, &clock),
-        [mem](Mono_Time *ptr) { mono_time_free(mem, ptr); });
+        mono_time_new(mem, tm.get()), [mem](Mono_Time *ptr) { mono_time_free(mem, ptr); });
     assert(mono_time != nullptr);
     std::unique_ptr<GC_Announces_List, void (*)(GC_Announces_List *)> gca(
         new_gca_list(mem), kill_gca);
diff --git a/toxcore/group_announce_test.cc b/toxcore/group_announce_test.cc
index 1b37fd33442..1440c7fb1eb 100644
--- a/toxcore/group_announce_test.cc
+++ b/toxcore/group_announce_test.cc
@@ -5,15 +5,22 @@
 #include "DHT.h"
 #include "crypto_core.h"
 #include "logger.h"
+#include "mem.h"
 #include "mem_test_util.hh"
 #include "mono_time.h"
 #include "network.h"
+#include "os_memory.h"
+#include "tox_time_impl.h"
 
 namespace {
 
 struct Announces : ::testing::Test {
 protected:
     Test_Memory mem_;
+    static constexpr Tox_Time_Funcs mock_time_funcs = {
+        [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
+    };
+    Tox_Time *tm_;
     uint64_t clock_ = 1000;
     Mono_Time *mono_time_ = nullptr;
     GC_Announces_List *gca_ = nullptr;
@@ -22,11 +29,11 @@ struct Announces : ::testing::Test {
 
     void SetUp() override
     {
-        mono_time_ = mono_time_new(mem_, nullptr, nullptr);
+        ASSERT_NE(mem_, nullptr);
+        tm_ = tox_time_new(&mock_time_funcs, &this->clock_, mem_);
+        ASSERT_NE(tm_, nullptr);
+        mono_time_ = mono_time_new(mem_, tm_);
         ASSERT_NE(mono_time_, nullptr);
-        mono_time_set_current_time_callback(
-            mono_time_, [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
-            &clock_);
         gca_ = new_gca_list(mem_);
         ASSERT_NE(gca_, nullptr);
     }
@@ -35,6 +42,7 @@ struct Announces : ::testing::Test {
     {
         kill_gca(gca_);
         mono_time_free(mem_, mono_time_);
+        tox_time_free(tm_);
     }
 
     void advance_clock(uint64_t increment)
diff --git a/toxcore/group_connection.c b/toxcore/group_connection.c
index 79c982959fb..e9c1d678d08 100644
--- a/toxcore/group_connection.c
+++ b/toxcore/group_connection.c
@@ -30,8 +30,7 @@
 #define GCC_UDP_DIRECT_TIMEOUT (GC_PING_TIMEOUT + 4)
 
 /** Returns true if array entry does not contain an active packet. */
-non_null()
-static bool array_entry_is_empty(const GC_Message_Array_Entry *array_entry)
+non_null() static bool array_entry_is_empty(const GC_Message_Array_Entry *array_entry)
 {
     assert(array_entry != nullptr);
     return array_entry->time_added == 0;
@@ -156,8 +155,8 @@ static bool add_to_send_array(const Logger *log, const Memory *mem, const Mono_T
     return true;
 }
 
-int gcc_send_lossless_packet(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data, uint16_t length,
-                             uint8_t packet_type)
+int gcc_send_lossless_packet(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data,
+                             uint16_t length, uint8_t packet_type)
 {
     const uint64_t message_id = gconn->send_message_id;
 
@@ -181,8 +180,8 @@ int gcc_send_lossless_packet(const GC_Chat *chat, GC_Connection *gconn, const ui
     return 0;
 }
 
-bool gcc_send_lossless_packet_fragments(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data,
-                                        uint16_t length, uint8_t packet_type)
+bool gcc_send_lossless_packet_fragments(const GC_Chat *chat, GC_Connection *gconn,
+                                        const uint8_t *data, uint16_t length, uint8_t packet_type)
 {
     if (length <= MAX_GC_PACKET_CHUNK_SIZE || data == nullptr) {
         LOGGER_FATAL(chat->log, "invalid length or null data pointer");
@@ -234,8 +233,8 @@ bool gcc_send_lossless_packet_fragments(const GC_Chat *chat, GC_Connection *gcon
 
         assert(entry->packet_type == GP_FRAGMENT);
 
-        gcc_encrypt_and_send_lossless_packet(chat, gconn, entry->data, entry->data_length,
-                                             entry->message_id, entry->packet_type);
+        gcc_encrypt_and_send_lossless_packet(
+            chat, gconn, entry->data, entry->data_length, entry->message_id, entry->packet_type);
     }
 
     return true;
@@ -378,7 +377,8 @@ static uint16_t reassemble_packet(const Logger *log, const Memory *mem, GC_Conne
         packet_length = diff;
 
         if (packet_length > MAX_GC_PACKET_SIZE) {
-            LOGGER_ERROR(log, "Payload of size %u exceeded max packet size", packet_length);  // should never happen
+            LOGGER_ERROR(log, "Payload of size %u exceeded max packet size",
+                         packet_length);  // should never happen
             return 0;
         }
 
@@ -484,7 +484,7 @@ int gcc_handle_received_message(const Logger *log, const Memory *mem, const Mono
         return 0;
     }
 
-    if (packet_type == GP_FRAGMENT) { // we handle packet fragments as a special case
+    if (packet_type == GP_FRAGMENT) {  // we handle packet fragments as a special case
         return 3;
     }
 
@@ -508,15 +508,15 @@ int gcc_handle_received_message(const Logger *log, const Memory *mem, const Mono
  *
  * Return true on success.
  */
-non_null(1, 2, 3, 5) nullable(6)
-static bool process_recv_array_entry(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
-                                     GC_Message_Array_Entry *const array_entry, void *userdata)
+non_null(1, 2, 3, 5) nullable(6) static bool process_recv_array_entry(const GC_Session *c,
+        GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
+        GC_Message_Array_Entry *const array_entry, void *userdata)
 {
     uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE];
     memcpy(sender_pk, get_enc_key(&gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE);
 
-    const bool ret = handle_gc_lossless_helper(c, chat, peer_number, array_entry->data, array_entry->data_length,
-                     array_entry->packet_type, userdata);
+    const bool ret = handle_gc_lossless_helper(c, chat, peer_number, array_entry->data,
+                     array_entry->data_length, array_entry->packet_type, userdata);
 
     /* peer number can change from peer add operations in packet handlers */
     peer_number = get_peer_number_of_enc_pk(chat, sender_pk, false);
@@ -540,8 +540,8 @@ static bool process_recv_array_entry(const GC_Session *c, GC_Chat *chat, GC_Conn
     return true;
 }
 
-void gcc_check_recv_array(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
-                          void *userdata)
+void gcc_check_recv_array(
+    const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number, void *userdata)
 {
     if (gconn->last_chunk_id != 0) {  // dont check array if we have an unfinished fragment sequence
         return;
@@ -589,13 +589,14 @@ void gcc_resend_packets(const GC_Chat *chat, GC_Connection *gconn)
 
         /* if this occurrs less than once per second this won't be reliable */
         if (delta > 1 && is_power_of_2(delta)) {
-            gcc_encrypt_and_send_lossless_packet(chat, gconn, array_entry->data, array_entry->data_length,
-                                                 array_entry->message_id, array_entry->packet_type);
+            gcc_encrypt_and_send_lossless_packet(chat, gconn, array_entry->data,
+                                                 array_entry->data_length, array_entry->message_id, array_entry->packet_type);
         }
     }
 }
 
-bool gcc_send_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *packet, uint16_t length)
+bool gcc_send_packet(
+    const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *packet, uint16_t length)
 {
     if (packet == nullptr || length == 0) {
         return false;
@@ -605,20 +606,21 @@ bool gcc_send_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint
 
     if (gcc_direct_conn_is_possible(chat, gconn)) {
         if (gcc_conn_is_direct(chat->mono_time, gconn)) {
-            return (uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length;
+            return (uint16_t)sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length;
         }
 
-        if ((uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length) {
+        if ((uint16_t)sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length) {
             direct_send_attempt = true;
         }
     }
 
-    const int ret = send_packet_tcp_connection(chat->tcp_conn, gconn->tcp_connection_num, packet, length);
+    const int ret
+        = send_packet_tcp_connection(chat->tcp_conn, gconn->tcp_connection_num, packet, length);
     return ret == 0 || direct_send_attempt;
 }
 
-int gcc_encrypt_and_send_lossless_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *data,
-        uint16_t length, uint64_t message_id, uint8_t packet_type)
+int gcc_encrypt_and_send_lossless_packet(const GC_Chat *chat, const GC_Connection *gconn,
+        const uint8_t *data, uint16_t length, uint64_t message_id, uint8_t packet_type)
 {
     const uint16_t packet_size = gc_get_wrapped_packet_size(length, NET_PACKET_GC_LOSSLESS);
     uint8_t *packet = (uint8_t *)mem_balloc(chat->mem, packet_size);
@@ -661,7 +663,8 @@ bool gcc_conn_is_direct(const Mono_Time *mono_time, const GC_Connection *gconn)
 
 bool gcc_direct_conn_is_possible(const GC_Chat *chat, const GC_Connection *gconn)
 {
-    return !net_family_is_unspec(gconn->addr.ip_port.ip.family) && !net_family_is_unspec(net_family(chat->net));
+    return !net_family_is_unspec(gconn->addr.ip_port.ip.family)
+           && !net_family_is_unspec(net_family(chat->net));
 }
 
 void gcc_mark_for_deletion(GC_Connection *gconn, TCP_Connections *tcp_conn, Group_Exit_Type type,
@@ -680,7 +683,7 @@ void gcc_mark_for_deletion(GC_Connection *gconn, TCP_Connections *tcp_conn, Grou
 
     kill_tcp_connection_to(tcp_conn, gconn->tcp_connection_num);
 
-    if (length > 0 && length <= MAX_GC_PART_MESSAGE_SIZE  && part_message != nullptr) {
+    if (length > 0 && length <= MAX_GC_PART_MESSAGE_SIZE && part_message != nullptr) {
         memcpy(gconn->exit_info.part_message, part_message, length);
         gconn->exit_info.length = length;
     }
diff --git a/toxcore/list_test.cc b/toxcore/list_test.cc
index 794c5e23371..b7e16810035 100644
--- a/toxcore/list_test.cc
+++ b/toxcore/list_test.cc
@@ -3,10 +3,16 @@
 #include <gtest/gtest.h>
 
 #include "mem.h"
+#include "os_memory.h"
 
 namespace {
 
-TEST(List, CreateAndDestroyWithNonZeroSize)
+struct List : ::testing::Test {
+protected:
+    const Memory *mem_ = os_memory();
+};
+
+TEST_F(List, CreateAndDestroyWithNonZeroSize)
 {
     const Memory *mem = os_memory();
     BS_List list;
@@ -14,7 +20,7 @@ TEST(List, CreateAndDestroyWithNonZeroSize)
     bs_list_free(&list);
 }
 
-TEST(List, CreateAndDestroyWithZeroSize)
+TEST_F(List, CreateAndDestroyWithZeroSize)
 {
     const Memory *mem = os_memory();
     BS_List list;
@@ -22,7 +28,7 @@ TEST(List, CreateAndDestroyWithZeroSize)
     bs_list_free(&list);
 }
 
-TEST(List, DeleteFromEmptyList)
+TEST_F(List, DeleteFromEmptyList)
 {
     const Memory *mem = os_memory();
     BS_List list;
diff --git a/toxcore/logger.c b/toxcore/logger.c
index b97ef8e184e..5658fdc5f00 100644
--- a/toxcore/logger.c
+++ b/toxcore/logger.c
@@ -11,11 +11,12 @@
 #include <assert.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include <stdlib.h>
+#include <stdlib.h>  // IWYU pragma: keep
 #include <string.h>
 
 #include "ccompat.h"
 #include "mem.h"
+#include "mem.h"
 
 struct Logger {
     const Memory *mem;
diff --git a/toxcore/mem.c b/toxcore/mem.c
index 32e7eec07c7..ce33fb4a016 100644
--- a/toxcore/mem.c
+++ b/toxcore/mem.c
@@ -5,63 +5,29 @@
 
 #include "mem.h"
 
-#include <stdlib.h>
+#include <string.h>
 
-#include "attributes.h"
 #include "ccompat.h"
-
-nullable(1)
-static void *sys_malloc(void *obj, uint32_t size)
-{
-    return malloc(size);
-}
-
-nullable(1)
-static void *sys_calloc(void *obj, uint32_t nmemb, uint32_t size)
-{
-    return calloc(nmemb, size);
-}
-
-nullable(1, 2)
-static void *sys_realloc(void *obj, void *ptr, uint32_t size)
-{
-    return realloc(ptr, size);
-}
-
-nullable(1, 2)
-static void sys_free(void *obj, void *ptr)
-{
-    free(ptr);
-}
-
-static const Memory_Funcs os_memory_funcs = {
-    sys_malloc,
-    sys_calloc,
-    sys_realloc,
-    sys_free,
-};
-static const Memory os_memory_obj = {&os_memory_funcs};
-
-const Memory *os_memory(void)
-{
-    return &os_memory_obj;
-}
+#include "tox_memory.h"
 
 void *mem_balloc(const Memory *mem, uint32_t size)
 {
-    void *const ptr = mem->funcs->malloc(mem->obj, size);
+    void *const ptr = tox_memory_malloc(mem, size);
     return ptr;
 }
 
 void *mem_brealloc(const Memory *mem, void *ptr, uint32_t size)
 {
-    void *const new_ptr = mem->funcs->realloc(mem->obj, ptr, size);
+    void *const new_ptr = tox_memory_realloc(mem, ptr, size);
     return new_ptr;
 }
 
 void *mem_alloc(const Memory *mem, uint32_t size)
 {
-    void *const ptr = mem->funcs->calloc(mem->obj, 1, size);
+    void *const ptr = tox_memory_malloc(mem, size);
+    if (ptr != nullptr) {
+        memset(ptr, 0, size);
+    }
     return ptr;
 }
 
@@ -73,7 +39,10 @@ void *mem_valloc(const Memory *mem, uint32_t nmemb, uint32_t size)
         return nullptr;
     }
 
-    void *const ptr = mem->funcs->calloc(mem->obj, nmemb, size);
+    void *const ptr = tox_memory_malloc(mem, bytes);
+    if (ptr != nullptr) {
+        memset(ptr, 0, bytes);
+    }
     return ptr;
 }
 
@@ -85,11 +54,11 @@ void *mem_vrealloc(const Memory *mem, void *ptr, uint32_t nmemb, uint32_t size)
         return nullptr;
     }
 
-    void *const new_ptr = mem->funcs->realloc(mem->obj, ptr, bytes);
+    void *const new_ptr = tox_memory_realloc(mem, ptr, bytes);
     return new_ptr;
 }
 
 void mem_delete(const Memory *mem, void *ptr)
 {
-    mem->funcs->free(mem->obj, ptr);
+    tox_memory_dealloc(mem, ptr);
 }
diff --git a/toxcore/mem.h b/toxcore/mem.h
index 6c36027ce71..fbf59015496 100644
--- a/toxcore/mem.h
+++ b/toxcore/mem.h
@@ -12,30 +12,13 @@
 #include <stdint.h>     // uint*_t
 
 #include "attributes.h"
+#include "tox_memory.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef void *mem_malloc_cb(void *obj, uint32_t size);
-typedef void *mem_calloc_cb(void *obj, uint32_t nmemb, uint32_t size);
-typedef void *mem_realloc_cb(void *obj, void *ptr, uint32_t size);
-typedef void mem_free_cb(void *obj, void *ptr);
-
-/** @brief Functions wrapping standard C memory allocation functions. */
-typedef struct Memory_Funcs {
-    mem_malloc_cb *malloc;
-    mem_calloc_cb *calloc;
-    mem_realloc_cb *realloc;
-    mem_free_cb *free;
-} Memory_Funcs;
-
-typedef struct Memory {
-    const Memory_Funcs *funcs;
-    void *obj;
-} Memory;
-
-const Memory *os_memory(void);
+typedef Tox_Memory Memory;
 
 /**
  * @brief Allocate an array of a given size for built-in types.
diff --git a/toxcore/mem_test.cc b/toxcore/mem_test.cc
index 5b649e21132..bdd567cf07e 100644
--- a/toxcore/mem_test.cc
+++ b/toxcore/mem_test.cc
@@ -2,6 +2,8 @@
 
 #include <gtest/gtest.h>
 
+#include "os_memory.h"
+
 namespace {
 
 TEST(Mem, AllocLarge)
@@ -25,15 +27,15 @@ TEST(Mem, AllocOverflow)
     const Memory *mem = os_memory();
 
     // 1 gibi-elements of 100 bytes each.
-    void *ptr = mem_valloc(mem, GI, 100);
+    void *ptr = mem_vrealloc(mem, nullptr, GI, 100);
     EXPECT_EQ(ptr, nullptr);
 
     // 100 elements of 1 gibibyte each.
-    ptr = mem_valloc(mem, 100, GI);
+    ptr = mem_vrealloc(mem, nullptr, 100, GI);
     EXPECT_EQ(ptr, nullptr);
 
     // 128 (a multiple of 2) elements of 1 gibibyte each.
-    ptr = mem_valloc(mem, 128, GI);
+    ptr = mem_vrealloc(mem, nullptr, 128, GI);
     EXPECT_EQ(ptr, nullptr);
 }
 
diff --git a/toxcore/mem_test_util.cc b/toxcore/mem_test_util.cc
index 02ade8fc684..e6288710306 100644
--- a/toxcore/mem_test_util.cc
+++ b/toxcore/mem_test_util.cc
@@ -2,28 +2,28 @@
 
 #include <cstdlib>
 
-#include "mem.h"
 #include "test_util.hh"
+#include "tox_memory_impl.h"
 
-Memory_Funcs const Memory_Class::vtable = {
-    Method<mem_malloc_cb, Memory_Class>::invoke<&Memory_Class::malloc>,
-    Method<mem_calloc_cb, Memory_Class>::invoke<&Memory_Class::calloc>,
-    Method<mem_realloc_cb, Memory_Class>::invoke<&Memory_Class::realloc>,
-    Method<mem_free_cb, Memory_Class>::invoke<&Memory_Class::free>,
+Tox_Memory_Funcs const Memory_Class::vtable = {
+    Method<tox_memory_malloc_cb, Memory_Class>::invoke<&Memory_Class::malloc>,
+    Method<tox_memory_realloc_cb, Memory_Class>::invoke<&Memory_Class::realloc>,
+    Method<tox_memory_dealloc_cb, Memory_Class>::invoke<&Memory_Class::dealloc>,
 };
 
 Memory_Class::~Memory_Class() = default;
 
-void *Test_Memory::malloc(void *obj, uint32_t size) { return mem->funcs->malloc(mem->obj, size); }
-
-void *Test_Memory::calloc(void *obj, uint32_t nmemb, uint32_t size)
+void *Test_Memory::malloc(void *obj, uint32_t size)
 {
-    return mem->funcs->calloc(mem->obj, nmemb, size);
+    return mem->funcs->malloc_callback(mem->user_data, size);
 }
 
 void *Test_Memory::realloc(void *obj, void *ptr, uint32_t size)
 {
-    return mem->funcs->realloc(mem->obj, ptr, size);
+    return mem->funcs->realloc_callback(mem->user_data, ptr, size);
 }
 
-void Test_Memory::free(void *obj, void *ptr) { return mem->funcs->free(mem->obj, ptr); }
+void Test_Memory::dealloc(void *obj, void *ptr)
+{
+    return mem->funcs->dealloc_callback(mem->user_data, ptr);
+}
diff --git a/toxcore/mem_test_util.hh b/toxcore/mem_test_util.hh
index 03094eb5787..7a02a22e303 100644
--- a/toxcore/mem_test_util.hh
+++ b/toxcore/mem_test_util.hh
@@ -2,13 +2,15 @@
 #define C_TOXCORE_TOXCORE_MEM_TEST_UTIL_H
 
 #include "mem.h"
+#include "os_memory.h"
 #include "test_util.hh"
+#include "tox_memory_impl.h"
 
 struct Memory_Class {
-    static Memory_Funcs const vtable;
-    Memory const self;
+    static Tox_Memory_Funcs const vtable;
+    Tox_Memory const self;
 
-    operator Memory const *() const { return &self; }
+    operator Tox_Memory const *() const { return &self; }
 
     Memory_Class(Memory_Class const &) = default;
     Memory_Class()
@@ -17,10 +19,9 @@ struct Memory_Class {
     }
 
     virtual ~Memory_Class();
-    virtual mem_malloc_cb malloc = 0;
-    virtual mem_calloc_cb calloc = 0;
-    virtual mem_realloc_cb realloc = 0;
-    virtual mem_free_cb free = 0;
+    virtual tox_memory_malloc_cb malloc = 0;
+    virtual tox_memory_realloc_cb realloc = 0;
+    virtual tox_memory_dealloc_cb dealloc = 0;
 };
 
 /**
@@ -28,12 +29,11 @@ struct Memory_Class {
  * subclassed to override individual (or all) functions.
  */
 class Test_Memory : public Memory_Class {
-    const Memory *mem = REQUIRE_NOT_NULL(os_memory());
+    const Tox_Memory *mem = REQUIRE_NOT_NULL(os_memory());
 
     void *malloc(void *obj, uint32_t size) override;
-    void *calloc(void *obj, uint32_t nmemb, uint32_t size) override;
     void *realloc(void *obj, void *ptr, uint32_t size) override;
-    void free(void *obj, void *ptr) override;
+    void dealloc(void *obj, void *ptr) override;
 };
 
 #endif  // C_TOXCORE_TOXCORE_MEM_TEST_UTIL_H
diff --git a/toxcore/mono_time.c b/toxcore/mono_time.c
index 8a3044c6f04..e408af848e3 100644
--- a/toxcore/mono_time.c
+++ b/toxcore/mono_time.c
@@ -6,35 +6,16 @@
 #define _XOPEN_SOURCE 600
 #endif /* _XOPEN_SOURCE */
 
-#if !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32))
-#define OS_WIN32
-#endif /* WIN32 */
-
 #include "mono_time.h"
 
-#ifdef OS_WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#endif /* OS_WIN32 */
-
-#ifdef __APPLE__
-#include <mach/clock.h>
-#include <mach/mach.h>
-#endif /* __APPLE__ */
-
-#ifndef OS_WIN32
-#include <sys/time.h>
-#endif /* OS_WIN32 */
-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-#include <assert.h>
-#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
 #include <pthread.h>
 #include <time.h>
 
 #include "attributes.h"
 #include "ccompat.h"
 #include "mem.h"
+#include "os_time.h"
+#include "tox_time.h"
 #include "util.h"
 
 /** don't call into system billions of times for no reason */
@@ -47,70 +28,10 @@ struct Mono_Time {
     pthread_rwlock_t *time_update_lock;
 #endif /* ESP_PLATFORM */
 
-    mono_time_current_time_cb *current_time_callback;
-    void *user_data;
+    const Tox_Time *tm;
 };
 
-static uint64_t timespec_to_u64(struct timespec clock_mono)
-{
-    return UINT64_C(1000) * clock_mono.tv_sec + (clock_mono.tv_nsec / UINT64_C(1000000));
-}
-
-#ifdef OS_WIN32
-non_null()
-static uint64_t current_time_monotonic_default(void *user_data)
-{
-    LARGE_INTEGER freq;
-    LARGE_INTEGER count;
-    if (!QueryPerformanceFrequency(&freq)) {
-        return 0;
-    }
-    if (!QueryPerformanceCounter(&count)) {
-        return 0;
-    }
-    struct timespec sp = {0};
-    sp.tv_sec = count.QuadPart / freq.QuadPart;
-    if (freq.QuadPart < 1000000000) {
-        sp.tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
-    } else {
-        sp.tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
-    }
-    return timespec_to_u64(sp);
-}
-#else
-#ifdef __APPLE__
-non_null()
-static uint64_t current_time_monotonic_default(void *user_data)
-{
-    struct timespec clock_mono;
-    clock_serv_t muhclock;
-    mach_timespec_t machtime;
-
-    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &muhclock);
-    clock_get_time(muhclock, &machtime);
-    mach_port_deallocate(mach_task_self(), muhclock);
-
-    clock_mono.tv_sec = machtime.tv_sec;
-    clock_mono.tv_nsec = machtime.tv_nsec;
-    return timespec_to_u64(clock_mono);
-}
-#else // !__APPLE__
-non_null()
-static uint64_t current_time_monotonic_default(void *user_data)
-{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-    // This assert should always fail. If it does, the fuzzing harness didn't
-    // override the mono time callback.
-    assert(user_data == nullptr);
-#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
-    struct timespec clock_mono;
-    clock_gettime(CLOCK_MONOTONIC, &clock_mono);
-    return timespec_to_u64(clock_mono);
-}
-#endif /* !__APPLE__ */
-#endif /* !OS_WIN32 */
-
-Mono_Time *mono_time_new(const Memory *mem, mono_time_current_time_cb *current_time_callback, void *user_data)
+Mono_Time *mono_time_new(const Memory *mem, const Tox_Time *tm)
 {
     Mono_Time *mono_time = (Mono_Time *)mem_alloc(mem, sizeof(Mono_Time));
 
@@ -135,7 +56,7 @@ Mono_Time *mono_time_new(const Memory *mem, mono_time_current_time_cb *current_t
     mono_time->time_update_lock = rwlock;
 #endif /* ESP_PLATFORM */
 
-    mono_time_set_current_time_callback(mono_time, current_time_callback, user_data);
+    mono_time_set_current_time_callback(mono_time, tm);
 
     mono_time->cur_time = 0;
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
@@ -166,8 +87,7 @@ void mono_time_free(const Memory *mem, Mono_Time *mono_time)
 
 void mono_time_update(Mono_Time *mono_time)
 {
-    const uint64_t cur_time =
-        mono_time->base_time + mono_time->current_time_callback(mono_time->user_data);
+    const uint64_t cur_time = tox_time_monotonic(mono_time->tm) + mono_time->base_time;
 
 #ifndef ESP_PLATFORM
     pthread_rwlock_wrlock(mono_time->time_update_lock);
@@ -201,16 +121,9 @@ bool mono_time_is_timeout(const Mono_Time *mono_time, uint64_t timestamp, uint64
     return timestamp + timeout <= mono_time_get(mono_time);
 }
 
-void mono_time_set_current_time_callback(Mono_Time *mono_time,
-        mono_time_current_time_cb *current_time_callback, void *user_data)
+void mono_time_set_current_time_callback(Mono_Time *mono_time, const Tox_Time *tm)
 {
-    if (current_time_callback == nullptr) {
-        mono_time->current_time_callback = current_time_monotonic_default;
-        mono_time->user_data = mono_time;
-    } else {
-        mono_time->current_time_callback = current_time_callback;
-        mono_time->user_data = user_data;
-    }
+    mono_time->tm = tm != nullptr ? tm : os_time();
 }
 
 /** @brief Return current monotonic time in milliseconds (ms).
@@ -220,5 +133,5 @@ void mono_time_set_current_time_callback(Mono_Time *mono_time,
  */
 uint64_t current_time_monotonic(const Mono_Time *mono_time)
 {
-    return mono_time->current_time_callback(mono_time->user_data);
+    return tox_time_monotonic(mono_time->tm);
 }
diff --git a/toxcore/mono_time.h b/toxcore/mono_time.h
index e23c1ba0df0..68a1e3fbdb1 100644
--- a/toxcore/mono_time.h
+++ b/toxcore/mono_time.h
@@ -10,6 +10,7 @@
 
 #include "attributes.h"
 #include "mem.h"
+#include "tox_time.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -46,10 +47,8 @@ extern "C" {
  */
 typedef struct Mono_Time Mono_Time;
 
-typedef uint64_t mono_time_current_time_cb(void *user_data);
-
-non_null(1) nullable(2, 3)
-Mono_Time *mono_time_new(const Memory *mem, mono_time_current_time_cb *current_time_callback, void *user_data);
+non_null(1) nullable(2)
+Mono_Time *mono_time_new(const Memory *mem, const Tox_Time *tm);
 
 non_null(1) nullable(2)
 void mono_time_free(const Memory *mem, Mono_Time *mono_time);
@@ -95,9 +94,8 @@ uint64_t current_time_monotonic(const Mono_Time *mono_time);
  * The caller is obligated to ensure that `current_time_monotonic()` continues
  * to increase monotonically.
  */
-non_null(1) nullable(2, 3)
-void mono_time_set_current_time_callback(Mono_Time *mono_time,
-        mono_time_current_time_cb *current_time_callback, void *user_data);
+non_null(1) nullable(2)
+void mono_time_set_current_time_callback(Mono_Time *mono_time, const Tox_Time *tm);
 
 #ifdef __cplusplus
 } /* extern "C" */
diff --git a/toxcore/mono_time_test.cc b/toxcore/mono_time_test.cc
index 0334f323229..80e883b2b85 100644
--- a/toxcore/mono_time_test.cc
+++ b/toxcore/mono_time_test.cc
@@ -6,13 +6,14 @@
 #include <thread>
 
 #include "mem_test_util.hh"
+#include "tox_time_impl.h"
 
 namespace {
 
 TEST(MonoTime, UnixTimeIncreasesOverTime)
 {
     Test_Memory mem;
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     ASSERT_NE(mono_time, nullptr);
 
     mono_time_update(mono_time);
@@ -31,7 +32,7 @@ TEST(MonoTime, UnixTimeIncreasesOverTime)
 TEST(MonoTime, IsTimeout)
 {
     Test_Memory mem;
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     ASSERT_NE(mono_time, nullptr);
 
     uint64_t const start = mono_time_get(mono_time);
@@ -49,7 +50,7 @@ TEST(MonoTime, IsTimeout)
 TEST(MonoTime, IsTimeoutReal)
 {
     Test_Memory mem;
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     ASSERT_NE(mono_time, nullptr);
 
     uint64_t const start = mono_time_get(mono_time);
@@ -70,13 +71,17 @@ TEST(MonoTime, IsTimeoutReal)
 TEST(MonoTime, CustomTime)
 {
     Test_Memory mem;
-    Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr);
+    Mono_Time *mono_time = mono_time_new(mem, nullptr);
     ASSERT_NE(mono_time, nullptr);
 
     uint64_t test_time = current_time_monotonic(mono_time) + 42137;
 
-    mono_time_set_current_time_callback(
-        mono_time, [](void *user_data) { return *static_cast<uint64_t *>(user_data); }, &test_time);
+    constexpr Tox_Time_Funcs mock_time_funcs = {
+        [](void *user_data) { return *static_cast<uint64_t *>(user_data); },
+    };
+    Tox_Time *tm = tox_time_new(&mock_time_funcs, &test_time, mem);
+    ASSERT_NE(tm, nullptr);
+    mono_time_set_current_time_callback(mono_time, tm);
     mono_time_update(mono_time);
 
     EXPECT_EQ(current_time_monotonic(mono_time), test_time);
@@ -91,6 +96,7 @@ TEST(MonoTime, CustomTime)
     EXPECT_EQ(current_time_monotonic(mono_time), test_time);
 
     mono_time_free(mem, mono_time);
+    tox_time_free(tm);
 }
 
 }  // namespace
diff --git a/toxcore/network.c b/toxcore/network.c
index 396d56b2152..3798055982c 100644
--- a/toxcore/network.c
+++ b/toxcore/network.c
@@ -47,15 +47,9 @@
 #include <ws2tcpip.h>
 #endif /* OS_WIN32 */
 
-#ifdef __APPLE__
-#include <mach/clock.h>
-#include <mach/mach.h>
-#endif /* __APPLE__ */
-
 #if !defined(OS_WIN32)
 #include <arpa/inet.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <sys/ioctl.h>
@@ -86,14 +80,11 @@
 #include "ccompat.h"
 #include "logger.h"
 #include "mem.h"
+#include "os_network_impl.h"
+#include "tox_network.h"
 #include "net_profile.h"
 #include "util.h"
 
-// Disable MSG_NOSIGNAL on systems not supporting it, e.g. Windows, FreeBSD
-#if !defined(MSG_NOSIGNAL)
-#define MSG_NOSIGNAL 0
-#endif /* !defined(MSG_NOSIGNAL) */
-
 #ifndef IPV6_ADD_MEMBERSHIP
 #ifdef IPV6_JOIN_GROUP
 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
@@ -481,261 +472,16 @@ bool sock_valid(Socket sock)
     return sock.value != invalid_socket.value;
 }
 
-struct Network_Addr {
-    struct sockaddr_storage addr;
-    size_t size;
-};
-
-non_null()
-static int sys_close(void *obj, Socket sock)
-{
-#if defined(OS_WIN32)
-    return closesocket(net_socket_to_native(sock));
-#else  // !OS_WIN32
-    return close(net_socket_to_native(sock));
-#endif /* OS_WIN32 */
-}
-
-non_null()
-static Socket sys_accept(void *obj, Socket sock)
-{
-    return net_socket_from_native(accept(net_socket_to_native(sock), nullptr, nullptr));
-}
-
-non_null()
-static int sys_bind(void *obj, Socket sock, const Network_Addr *addr)
-{
-    return bind(net_socket_to_native(sock), (const struct sockaddr *)&addr->addr, addr->size);
-}
-
-non_null()
-static int sys_listen(void *obj, Socket sock, int backlog)
-{
-    return listen(net_socket_to_native(sock), backlog);
-}
-
-non_null()
-static int sys_connect(void *obj, Socket sock, const Network_Addr *addr)
-{
-    return connect(net_socket_to_native(sock), (const struct sockaddr *)&addr->addr, addr->size);
-}
-
-non_null()
-static int sys_recvbuf(void *obj, Socket sock)
-{
-#ifdef OS_WIN32
-    u_long count = 0;
-    ioctlsocket(net_socket_to_native(sock), FIONREAD, &count);
-#else
-    int count = 0;
-    ioctl(net_socket_to_native(sock), FIONREAD, &count);
-#endif /* OS_WIN32 */
-
-    return count;
-}
-
-non_null()
-static int sys_recv(void *obj, Socket sock, uint8_t *buf, size_t len)
-{
-    return recv(net_socket_to_native(sock), (char *)buf, len, MSG_NOSIGNAL);
-}
-
-non_null()
-static int sys_send(void *obj, Socket sock, const uint8_t *buf, size_t len)
-{
-    return send(net_socket_to_native(sock), (const char *)buf, len, MSG_NOSIGNAL);
-}
-
-non_null()
-static int sys_sendto(void *obj, Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr)
-{
-    return sendto(net_socket_to_native(sock), (const char *)buf, len, 0, (const struct sockaddr *)&addr->addr, addr->size);
-}
-
-non_null()
-static int sys_recvfrom(void *obj, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr)
-{
-    socklen_t size = addr->size;
-    const int ret = recvfrom(net_socket_to_native(sock), (char *)buf, len, 0, (struct sockaddr *)&addr->addr, &size);
-    addr->size = size;
-    return ret;
-}
-
-non_null()
-static Socket sys_socket(void *obj, int domain, int type, int proto)
-{
-    return net_socket_from_native(socket(domain, type, proto));
-}
-
-non_null()
-static int sys_socket_nonblock(void *obj, Socket sock, bool nonblock)
-{
-#ifdef OS_WIN32
-    u_long mode = nonblock ? 1 : 0;
-    return ioctlsocket(net_socket_to_native(sock), FIONBIO, &mode);
-#else
-    return fcntl(net_socket_to_native(sock), F_SETFL, O_NONBLOCK, nonblock ? 1 : 0);
-#endif /* OS_WIN32 */
-}
-
-non_null()
-static int sys_getsockopt(void *obj, Socket sock, int level, int optname, void *optval, size_t *optlen)
-{
-    socklen_t len = *optlen;
-    const int ret = getsockopt(net_socket_to_native(sock), level, optname, (char *)optval, &len);
-    *optlen = len;
-    return ret;
-}
-
-non_null()
-static int sys_setsockopt(void *obj, Socket sock, int level, int optname, const void *optval, size_t optlen)
-{
-    return setsockopt(net_socket_to_native(sock), level, optname, (const char *)optval, optlen);
-}
-
-// sets and fills an array of addrs for address
-// returns the number of entries in addrs
-non_null()
-static int sys_getaddrinfo(void *obj, const Memory *mem, const char *address, int family, int sock_type, Network_Addr **addrs)
-{
-    assert(addrs != nullptr);
-
-    struct addrinfo hints = {0};
-    hints.ai_family = family;
-
-
-    // different platforms favour a different field
-    // hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses.
-    hints.ai_socktype = sock_type;
-    // hints.ai_protocol = protocol;
-
-    struct addrinfo *infos = nullptr;
-
-    const int rc = getaddrinfo(address, nullptr, &hints, &infos);
-
-    // Lookup failed.
-    if (rc != 0) {
-        // TODO(Green-Sky): log error
-        return 0;
-    }
-
-    const int32_t max_count = INT32_MAX / sizeof(Network_Addr);
-
-    // we count number of "valid" results
-    int result = 0;
-    for (struct addrinfo *walker = infos; walker != nullptr && result < max_count; walker = walker->ai_next) {
-        if (walker->ai_family == family || family == AF_UNSPEC) {
-            ++result;
-        }
-
-        // do we need to check socktype/protocol?
-    }
-
-    assert(max_count >= result);
-
-    Network_Addr *tmp_addrs = (Network_Addr *)mem_valloc(mem, result, sizeof(Network_Addr));
-    if (tmp_addrs == nullptr) {
-        freeaddrinfo(infos);
-        return 0;
-    }
-
-    // now we fill in
-    int i = 0;
-    for (struct addrinfo *walker = infos; walker != nullptr; walker = walker->ai_next) {
-        if (walker->ai_family == family || family == AF_UNSPEC) {
-            tmp_addrs[i].size = sizeof(struct sockaddr_storage);
-            tmp_addrs[i].addr.ss_family = walker->ai_family;
-
-            // according to spec, storage is supposed to be large enough (and source shows they are)
-            // storage is 128 bytes
-            assert(walker->ai_addrlen <= tmp_addrs[i].size);
-
-            memcpy(&tmp_addrs[i].addr, walker->ai_addr, walker->ai_addrlen);
-            tmp_addrs[i].size = walker->ai_addrlen;
-
-            ++i;
-        }
-    }
-
-    assert(i == result);
-
-    freeaddrinfo(infos);
-
-    *addrs = tmp_addrs;
-
-    // number of entries in addrs
-    return result;
-}
-
-non_null()
-static int sys_freeaddrinfo(void *obj, const Memory *mem, Network_Addr *addrs)
-{
-    if (addrs == nullptr) {
-        return 0;
-    }
-
-    mem_delete(mem, addrs);
-
-    return 0;
-}
-
-static const Network_Funcs os_network_funcs = {
-    sys_close,
-    sys_accept,
-    sys_bind,
-    sys_listen,
-    sys_connect,
-    sys_recvbuf,
-    sys_recv,
-    sys_recvfrom,
-    sys_send,
-    sys_sendto,
-    sys_socket,
-    sys_socket_nonblock,
-    sys_getsockopt,
-    sys_setsockopt,
-    sys_getaddrinfo,
-    sys_freeaddrinfo,
-};
-static const Network os_network_obj = {&os_network_funcs, nullptr};
-
-const Network *os_network(void)
-{
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-    if ((true)) {
-        return nullptr;
-    }
-#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
-#ifdef OS_WIN32
-    WSADATA wsaData;
-
-    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
-        return nullptr;
-    }
-#endif /* OS_WIN32 */
-    return &os_network_obj;
-}
-
-#if 0
-/* TODO(iphydf): Call this from functions that use `os_network()`. */
-void os_network_deinit(const Network *ns)
-{
-#ifdef OS_WIN32
-    WSACleanup();
-#endif /* OS_WIN32 */
-}
-#endif /* 0 */
-
 non_null()
 static int net_setsockopt(const Network *ns, Socket sock, int level, int optname, const void *optval, size_t optlen)
 {
-    return ns->funcs->setsockopt(ns->obj, sock, level, optname, optval, optlen);
+    return tox_network_setsockopt(ns, sock, level, optname, optval, optlen);
 }
 
 non_null()
 static int net_getsockopt(const Network *ns, Socket sock, int level, int optname, void *optval, size_t *optlen)
 {
-    return ns->funcs->getsockopt(ns->obj, sock, level, optname, optval, optlen);
+    return tox_network_getsockopt(ns, sock, level, optname, optval, optlen);
 }
 
 non_null()
@@ -910,7 +656,7 @@ static void loglogdata(const Logger *log, const char *message, const uint8_t *bu
 int net_send(const Network *ns, const Logger *log,
              Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port, Net_Profile *net_profile)
 {
-    const int res = ns->funcs->send(ns->obj, sock, buf, len);
+    const int res = tox_network_send(ns, sock, buf, len);
 
     if (res > 0) {
         netprof_record_packet(net_profile, buf[0], res, PACKET_DIRECTION_SEND);
@@ -925,13 +671,13 @@ static int net_sendto(
     const Network *ns,
     Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr, const IP_Port *ip_port)
 {
-    return ns->funcs->sendto(ns->obj, sock, buf, len, addr);
+    return tox_network_sendto(ns, sock, buf, len, addr);
 }
 
 int net_recv(const Network *ns, const Logger *log,
              Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port)
 {
-    const int res = ns->funcs->recv(ns->obj, sock, buf, len);
+    const int res = tox_network_recv(ns, sock, buf, len);
     loglogdata(log, "=>T", buf, len, ip_port, res);
     return res;
 }
@@ -940,34 +686,34 @@ non_null()
 static int net_recvfrom(const Network *ns,
                         Socket sock, uint8_t *buf, size_t len, Network_Addr *addr)
 {
-    return ns->funcs->recvfrom(ns->obj, sock, buf, len, addr);
+    return tox_network_recvfrom(ns, sock, buf, len, addr);
 }
 
 int net_listen(const Network *ns, Socket sock, int backlog)
 {
-    return ns->funcs->listen(ns->obj, sock, backlog);
+    return tox_network_listen(ns, sock, backlog);
 }
 
 non_null()
 static int net_bind(const Network *ns, Socket sock, const Network_Addr *addr)
 {
-    return ns->funcs->bind(ns->obj, sock, addr);
+    return tox_network_bind(ns, sock, addr);
 }
 
 Socket net_accept(const Network *ns, Socket sock)
 {
-    return ns->funcs->accept(ns->obj, sock);
+    return tox_network_accept(ns, sock);
 }
 
 /** Close the socket. */
 void kill_sock(const Network *ns, Socket sock)
 {
-    ns->funcs->close(ns->obj, sock);
+    tox_network_close(ns, sock);
 }
 
 bool set_socket_nonblock(const Network *ns, Socket sock)
 {
-    return ns->funcs->socket_nonblock(ns->obj, sock, true) == 0;
+    return tox_network_socket_nonblock(ns, sock, true) == 0;
 }
 
 bool set_socket_nosigpipe(const Network *ns, Socket sock)
@@ -1078,34 +824,41 @@ int send_packet(const Networking_Core *net, const IP_Port *ip_port, Packet packe
         ipp_copy.ip.ip.v6 = ip6;
     }
 
-    Network_Addr addr;
+    Network_Addr *addr;
 
     if (net_family_is_ipv4(ipp_copy.ip.family)) {
-        struct sockaddr_in *const addr4 = (struct sockaddr_in *)&addr.addr;
+        struct sockaddr_in addr4 = {0};
 
-        addr.size = sizeof(struct sockaddr_in);
-        addr4->sin_family = AF_INET;
-        addr4->sin_port = ipp_copy.port;
-        fill_addr4(&ipp_copy.ip.ip.v4, &addr4->sin_addr);
+        addr4.sin_family = AF_INET;
+        addr4.sin_port = ipp_copy.port;
+        fill_addr4(&ipp_copy.ip.ip.v4, &addr4.sin_addr);
+
+        addr = net_addr_new(&addr4, sizeof(addr4), net->mem);
     } else if (net_family_is_ipv6(ipp_copy.ip.family)) {
-        struct sockaddr_in6 *const addr6 = (struct sockaddr_in6 *)&addr.addr;
+        struct sockaddr_in6 addr6 = {0};
 
-        addr.size = sizeof(struct sockaddr_in6);
-        addr6->sin6_family = AF_INET6;
-        addr6->sin6_port = ipp_copy.port;
-        fill_addr6(&ipp_copy.ip.ip.v6, &addr6->sin6_addr);
+        addr6.sin6_family = AF_INET6;
+        addr6.sin6_port = ipp_copy.port;
+        fill_addr6(&ipp_copy.ip.ip.v6, &addr6.sin6_addr);
 
-        addr6->sin6_flowinfo = 0;
-        addr6->sin6_scope_id = 0;
+        addr6.sin6_flowinfo = 0;
+        addr6.sin6_scope_id = 0;
+
+        addr = net_addr_new(&addr6, sizeof(addr6), net->mem);
     } else {
         LOGGER_ERROR(net->log, "unknown address type: %d", ipp_copy.ip.family.value);
         return -1;
     }
 
-    const long res = net_sendto(net->ns, net->sock, packet.data, packet.length, &addr, &ipp_copy);
+    if (addr == nullptr) {
+        return -1;
+    }
+
+    const long res = net_sendto(net->ns, net->sock, packet.data, packet.length, addr, &ipp_copy);
     loglogdata(net->log, "O=>", packet.data, packet.length, ip_port, res);
 
     assert(res <= INT_MAX);
+    net_addr_free(addr, net->mem);
 
     if (res == packet.length && packet.data != nullptr) {
         netprof_record_packet(net->udp_net_profile, packet.data[0], packet.length, PACKET_DIRECTION_SEND);
@@ -1134,11 +887,14 @@ non_null()
 static int receivepacket(const Network *ns, const Memory *mem, const Logger *log, Socket sock, IP_Port *ip_port, uint8_t *data, uint32_t *length)
 {
     memset(ip_port, 0, sizeof(IP_Port));
-    Network_Addr addr = {{0}};
-    addr.size = sizeof(addr.addr);
+    Network_Addr *addr = net_addr_new(nullptr, 0, mem);
     *length = 0;
 
-    const int fail_or_len = net_recvfrom(ns, sock, data, MAX_UDP_PACKET_SIZE, &addr);
+    if (addr == nullptr) {
+        return -1;
+    }
+
+    const int fail_or_len = net_recvfrom(ns, sock, data, MAX_UDP_PACKET_SIZE, addr);
 
     if (fail_or_len < 0) {
         const int error = net_error();
@@ -1149,30 +905,33 @@ static int receivepacket(const Network *ns, const Memory *mem, const Logger *log
             net_kill_strerror(strerror);
         }
 
+        net_addr_free(addr, mem);
         return -1; /* Nothing received. */
     }
 
     *length = (uint32_t)fail_or_len;
 
-    if (addr.addr.ss_family == AF_INET) {
-        const struct sockaddr_in *addr_in = (const struct sockaddr_in *)&addr.addr;
+    if (net_addr_is_ipv4(addr)) {
+        const struct sockaddr_in *addr_in = (const struct sockaddr_in *)net_addr_get_addr(addr);
 
         const Family *const family = make_tox_family(addr_in->sin_family);
         assert(family != nullptr);
 
         if (family == nullptr) {
+            net_addr_free(addr, mem);
             return -1;
         }
 
         ip_port->ip.family = *family;
         get_ip4(&ip_port->ip.ip.v4, &addr_in->sin_addr);
         ip_port->port = addr_in->sin_port;
-    } else if (addr.addr.ss_family == AF_INET6) {
-        const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)&addr.addr;
+    } else if (net_addr_is_ipv6(addr)) {
+        const struct sockaddr_in6 *addr_in6 = (const struct sockaddr_in6 *)net_addr_get_addr(addr);
         const Family *const family = make_tox_family(addr_in6->sin6_family);
         assert(family != nullptr);
 
         if (family == nullptr) {
+            net_addr_free(addr, mem);
             return -1;
         }
 
@@ -1185,11 +944,13 @@ static int receivepacket(const Network *ns, const Memory *mem, const Logger *log
             ip_port->ip.ip.v4.uint32 = ip_port->ip.ip.v6.uint32[3];
         }
     } else {
+        net_addr_free(addr, mem);
         return -1;
     }
 
     loglogdata(log, "=>O", data, MAX_UDP_PACKET_SIZE, ip_port, *length);
 
+    net_addr_free(addr, mem);
     return 0;
 }
 
@@ -1354,21 +1115,31 @@ Networking_Core *new_networking_ex(
 
     /* Bind our socket to port PORT and the given IP address (usually 0.0.0.0 or ::) */
     uint16_t *portptr = nullptr;
-    Network_Addr addr = {{0}};
+    Network_Addr *addr = net_addr_new(nullptr, 0, mem);
+
+    if (addr == nullptr) {
+        kill_networking(temp);
+
+        if (error != nullptr) {
+            *error = 2;
+        }
+
+        return nullptr;
+    }
 
     if (net_family_is_ipv4(temp->family)) {
-        struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr;
+        struct sockaddr_in *addr4 = (struct sockaddr_in *)net_addr_mut_addr(addr);
 
-        addr.size = sizeof(struct sockaddr_in);
+        net_addr_set_size(addr, sizeof(struct sockaddr_in));
         addr4->sin_family = AF_INET;
         addr4->sin_port = 0;
         fill_addr4(&ip->ip.v4, &addr4->sin_addr);
 
         portptr = &addr4->sin_port;
     } else if (net_family_is_ipv6(temp->family)) {
-        struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr;
+        struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)net_addr_mut_addr(addr);
 
-        addr.size = sizeof(struct sockaddr_in6);
+        net_addr_set_size(addr, sizeof(struct sockaddr_in6));
         addr6->sin6_family = AF_INET6;
         addr6->sin6_port = 0;
         fill_addr6(&ip->ip.v6, &addr6->sin6_addr);
@@ -1379,6 +1150,7 @@ Networking_Core *new_networking_ex(
         portptr = &addr6->sin6_port;
     } else {
         mem_delete(mem, temp);
+        net_addr_free(addr, mem);
         return nullptr;
     }
 
@@ -1434,7 +1206,7 @@ Networking_Core *new_networking_ex(
     *portptr = net_htons(port_to_try);
 
     for (uint16_t tries = port_from; tries <= port_to; ++tries) {
-        const int res = net_bind(ns, temp->sock, &addr);
+        const int res = net_bind(ns, temp->sock, addr);
 
         if (res == 0) {
             temp->port = *portptr;
@@ -1454,6 +1226,7 @@ Networking_Core *new_networking_ex(
                 *error = 0;
             }
 
+            net_addr_free(addr, mem);
             return temp;
         }
 
@@ -1478,6 +1251,7 @@ Networking_Core *new_networking_ex(
         *error = 1;
     }
 
+    net_addr_free(addr, mem);
     return nullptr;
 }
 
@@ -1958,7 +1732,7 @@ static bool addr_resolve(const Network *ns, const Memory *mem, const char *addre
     const int family = make_family(tox_family);
 
     Network_Addr *addrs = nullptr;
-    const int rc = ns->funcs->getaddrinfo(ns->obj, mem, address, family, 0, &addrs);
+    const int rc = tox_network_getaddrinfo(ns, mem, address, family, 0, &addrs);
 
     // Lookup failed / empty.
     if (rc <= 0) {
@@ -1976,16 +1750,18 @@ static bool addr_resolve(const Network *ns, const Memory *mem, const char *addre
     bool done = false;
 
     for (int i = 0; i < rc && !done; ++i) {
-        switch (addrs[i].addr.ss_family) {
+        const Network_Addr *const addr = net_addrs_get_addr(addrs, i);
+        const int addr_family = net_addr_get_family(addr);
+        switch (addr_family) {
             case AF_INET: {
-                if (addrs[i].addr.ss_family == family) { /* AF_INET requested, done */
-                    const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)&addrs[i].addr;
-                    get_ip4(&to->ip.v4, &addr->sin_addr);
+                if (addr_family == family) { /* AF_INET requested, done */
+                    const struct sockaddr_in *addr4 = (const struct sockaddr_in *)net_addr_get_addr(addr);
+                    get_ip4(&to->ip.v4, &addr4->sin_addr);
                     result = TOX_ADDR_RESOLVE_INET;
                     done = true;
                 } else if ((result & TOX_ADDR_RESOLVE_INET) == 0) { /* AF_UNSPEC requested, store away */
-                    const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)&addrs[i].addr;
-                    get_ip4(&ip4.ip.v4, &addr->sin_addr);
+                    const struct sockaddr_in *addr4 = (const struct sockaddr_in *)net_addr_get_addr(addr);
+                    get_ip4(&ip4.ip.v4, &addr4->sin_addr);
                     result |= TOX_ADDR_RESOLVE_INET;
                 }
 
@@ -1993,17 +1769,17 @@ static bool addr_resolve(const Network *ns, const Memory *mem, const char *addre
             }
 
             case AF_INET6: {
-                if (addrs[i].addr.ss_family == family) { /* AF_INET6 requested, done */
-                    if (addrs[i].size == sizeof(struct sockaddr_in6)) {
-                        const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(void *)&addrs[i].addr;
-                        get_ip6(&to->ip.v6, &addr->sin6_addr);
+                if (addr_family == family) { /* AF_INET6 requested, done */
+                    if (net_addr_get_size(addr) == sizeof(struct sockaddr_in6)) {
+                        const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)net_addr_get_addr(addr);
+                        get_ip6(&to->ip.v6, &addr6->sin6_addr);
                         result = TOX_ADDR_RESOLVE_INET6;
                         done = true;
                     }
                 } else if ((result & TOX_ADDR_RESOLVE_INET6) == 0) { /* AF_UNSPEC requested, store away */
-                    if (addrs[i].size == sizeof(struct sockaddr_in6)) {
-                        const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(void *)&addrs[i].addr;
-                        get_ip6(&ip6.ip.v6, &addr->sin6_addr);
+                    if (net_addr_get_size(addr) == sizeof(struct sockaddr_in6)) {
+                        const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)net_addr_get_addr(addr);
+                        get_ip6(&ip6.ip.v6, &addr6->sin6_addr);
                         result |= TOX_ADDR_RESOLVE_INET6;
                     }
                 }
@@ -2027,7 +1803,7 @@ static bool addr_resolve(const Network *ns, const Memory *mem, const char *addre
         }
     }
 
-    ns->funcs->freeaddrinfo(ns->obj, mem, addrs);
+    tox_network_freeaddrinfo(ns, mem, addrs);
     return result != 0;
 }
 
@@ -2056,19 +1832,23 @@ const char *net_err_connect_to_string(Net_Err_Connect err)
 
 bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket sock, const IP_Port *ip_port, Net_Err_Connect *err)
 {
-    Network_Addr addr = {{0}};
+    Network_Addr *addr = net_addr_new(ns, 0, mem);
+
+    if (addr == nullptr) {
+        return false;
+    }
 
     if (net_family_is_ipv4(ip_port->ip.family)) {
-        struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr;
+        struct sockaddr_in *addr4 = (struct sockaddr_in *)net_addr_mut_addr(addr);
 
-        addr.size = sizeof(struct sockaddr_in);
+        net_addr_set_size(addr, sizeof(struct sockaddr_in));
         addr4->sin_family = AF_INET;
         fill_addr4(&ip_port->ip.ip.v4, &addr4->sin_addr);
         addr4->sin_port = ip_port->port;
     } else if (net_family_is_ipv6(ip_port->ip.family)) {
-        struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr;
+        struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)net_addr_mut_addr(addr);
 
-        addr.size = sizeof(struct sockaddr_in6);
+        net_addr_set_size(addr, sizeof(struct sockaddr_in6));
         addr6->sin6_family = AF_INET6;
         fill_addr6(&ip_port->ip.ip.v6, &addr6->sin6_addr);
         addr6->sin6_port = ip_port->port;
@@ -2076,12 +1856,14 @@ bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket
         Ip_Ntoa ip_str;
         LOGGER_ERROR(log, "cannot connect to %s:%d which is neither IPv4 nor IPv6",
                      net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port));
+        net_addr_free(addr, mem);
         *err = NET_ERR_CONNECT_INVALID_FAMILY;
         return false;
     }
 
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     if ((true)) {
+        net_addr_free(addr, mem);
         *err = NET_ERR_CONNECT_OK;
         return true;
     }
@@ -2092,7 +1874,7 @@ bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket
                  net_socket_to_native(sock), net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port));
     errno = 0;
 
-    if (ns->funcs->connect(ns->obj, sock, &addr) == -1) {
+    if (tox_network_connect(ns, sock, addr) == -1) {
         const int error = net_error();
 
         // Non-blocking socket: "Operation in progress" means it's connecting.
@@ -2101,11 +1883,13 @@ bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket
             LOGGER_WARNING(log, "failed to connect to %s:%d: %d (%s)",
                            net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), error, net_strerror);
             net_kill_strerror(net_strerror);
+            net_addr_free(addr, mem);
             *err = NET_ERR_CONNECT_FAILED;
             return false;
         }
     }
 
+    net_addr_free(addr, mem);
     *err = NET_ERR_CONNECT_OK;
     return true;
 }
@@ -2124,6 +1908,7 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP
         IP_Port *tmp = (IP_Port *)mem_alloc(mem, sizeof(IP_Port));
 
         if (tmp == nullptr) {
+            *res = nullptr;
             return -1;
         }
 
@@ -2139,9 +1924,11 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     if ((true)) {
         IP_Port *ip_port = (IP_Port *)mem_alloc(mem, sizeof(IP_Port));
+
         if (ip_port == nullptr) {
-            abort();
+            return -1;
         }
+
         ip_port->ip.ip.v4.uint32 = net_htonl(0x7F000003); // 127.0.0.3
         ip_port->ip.family = *make_tox_family(AF_INET);
 
@@ -2158,7 +1945,7 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP
 
     // It's not an IP address, so now we try doing a DNS lookup.
     Network_Addr *addrs = nullptr;
-    const int rc = ns->funcs->getaddrinfo(ns->obj, mem, node, AF_UNSPEC, type, &addrs);
+    const int rc = tox_network_getaddrinfo(ns, mem, node, AF_UNSPEC, type, &addrs);
 
     // Lookup failed / empty.
     if (rc <= 0) {
@@ -2172,7 +1959,9 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP
     size_t count = 0;
 
     for (int i = 0; i < rc && count < max_count; ++i) {
-        if (addrs[i].addr.ss_family != AF_INET && addrs[i].addr.ss_family != AF_INET6) {
+        const Network_Addr *const addr = net_addrs_get_addr(addrs, i);
+        const int addr_family = net_addr_get_family(addr);
+        if (addr_family != AF_INET && addr_family != AF_INET6) {
             continue;
         }
 
@@ -2182,14 +1971,14 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP
     assert(count <= max_count);
 
     if (count == 0) {
-        ns->funcs->freeaddrinfo(ns->obj, mem, addrs);
+        tox_network_freeaddrinfo(ns, mem, addrs);
         return 0;
     }
 
     IP_Port *ip_port = (IP_Port *)mem_valloc(mem, count, sizeof(IP_Port));
 
     if (ip_port == nullptr) {
-        ns->funcs->freeaddrinfo(ns->obj, mem, addrs);
+        tox_network_freeaddrinfo(ns, mem, addrs);
         *res = nullptr;
         return -1;
     }
@@ -2197,21 +1986,23 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP
     *res = ip_port;
 
     for (int i = 0; i < rc && count < max_count; ++i) {
-        if (addrs[i].addr.ss_family == AF_INET) {
-            const struct sockaddr_in *addr = (const struct sockaddr_in *)(const void *)&addrs[i].addr;
-            ip_port->ip.ip.v4.uint32 = addr->sin_addr.s_addr;
-        } else if (addrs[i].addr.ss_family == AF_INET6) {
-            const struct sockaddr_in6 *addr = (const struct sockaddr_in6 *)(const void *)&addrs[i].addr;
-            memcpy(ip_port->ip.ip.v6.uint8, addr->sin6_addr.s6_addr, sizeof(IP6));
+        const Network_Addr *const addr = net_addrs_get_addr(addrs, i);
+        const int addr_family = net_addr_get_family(addr);
+        if (addr_family == AF_INET) {
+            const struct sockaddr_in *addr4 = (const struct sockaddr_in *)net_addr_get_addr(addr);
+            ip_port->ip.ip.v4.uint32 = addr4->sin_addr.s_addr;
+        } else if (addr_family == AF_INET6) {
+            const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)net_addr_get_addr(addr);
+            memcpy(ip_port->ip.ip.v6.uint8, addr6->sin6_addr.s6_addr, sizeof(IP6));
         } else {
             continue;
         }
 
-        const Family *const family = make_tox_family(addrs[i].addr.ss_family);
+        const Family *const family = make_tox_family(addr_family);
         assert(family != nullptr);
 
         if (family == nullptr) {
-            ns->funcs->freeaddrinfo(ns->obj, mem, addrs);
+            tox_network_freeaddrinfo(ns, mem, addrs);
             return -1;
         }
 
@@ -2220,7 +2011,7 @@ int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP
         ++ip_port;
     }
 
-    ns->funcs->freeaddrinfo(ns->obj, mem, addrs);
+    tox_network_freeaddrinfo(ns, mem, addrs);
 
     return count;
 }
@@ -2230,27 +2021,31 @@ void net_freeipport(const Memory *mem, IP_Port *ip_ports)
     mem_delete(mem, ip_ports);
 }
 
-bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port)
+bool bind_to_port(const Network *ns, const Memory *mem, Socket sock, Family family, uint16_t port)
 {
-    Network_Addr addr = {{0}};
+    Network_Addr *addr;
 
     if (net_family_is_ipv4(family)) {
-        struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr.addr;
+        struct sockaddr_in addr4 = {0};
 
-        addr.size = sizeof(struct sockaddr_in);
-        addr4->sin_family = AF_INET;
-        addr4->sin_port = net_htons(port);
+        addr4.sin_family = AF_INET;
+        addr4.sin_port = net_htons(port);
+
+        addr = net_addr_new(&addr4, sizeof(addr4), mem);
     } else if (net_family_is_ipv6(family)) {
-        struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr.addr;
+        struct sockaddr_in6 addr6 = {0};
 
-        addr.size = sizeof(struct sockaddr_in6);
-        addr6->sin6_family = AF_INET6;
-        addr6->sin6_port = net_htons(port);
+        addr6.sin6_family = AF_INET6;
+        addr6.sin6_port = net_htons(port);
+
+        addr = net_addr_new(&addr6, sizeof(addr6), mem);
     } else {
         return false;
     }
 
-    return net_bind(ns, sock, &addr) == 0;
+    const bool ok = net_bind(ns, sock, addr) == 0;
+    net_addr_free(addr, mem);
+    return ok;
 }
 
 Socket net_socket(const Network *ns, Family domain, int type, int protocol)
@@ -2258,12 +2053,12 @@ Socket net_socket(const Network *ns, Family domain, int type, int protocol)
     const int platform_domain = make_family(domain);
     const int platform_type = make_socktype(type);
     const int platform_prot = make_proto(protocol);
-    return ns->funcs->socket(ns->obj, platform_domain, platform_type, platform_prot);
+    return tox_network_socket(ns, platform_domain, platform_type, platform_prot);
 }
 
 uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock)
 {
-    const int count = ns->funcs->recvbuf(ns->obj, sock);
+    const int count = tox_network_recvbuf(ns, sock);
     return (uint16_t)max_s32(0, min_s32(count, UINT16_MAX));
 }
 
diff --git a/toxcore/network.h b/toxcore/network.h
index 41d7eafda9a..9e787fe0f6a 100644
--- a/toxcore/network.h
+++ b/toxcore/network.h
@@ -18,72 +18,12 @@
 #include "logger.h"
 #include "mem.h"
 #include "net_profile.h"
+#include "tox_network.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/**
- * @brief Wrapper for sockaddr_storage and size.
- */
-typedef struct Network_Addr Network_Addr;
-
-typedef bitwise int Socket_Value;
-typedef struct Socket {
-    Socket_Value value;
-} Socket;
-
-int net_socket_to_native(Socket sock);
-Socket net_socket_from_native(int sock);
-
-typedef int net_close_cb(void *obj, Socket sock);
-typedef Socket net_accept_cb(void *obj, Socket sock);
-typedef int net_bind_cb(void *obj, Socket sock, const Network_Addr *addr);
-typedef int net_listen_cb(void *obj, Socket sock, int backlog);
-typedef int net_connect_cb(void *obj, Socket sock, const Network_Addr *addr);
-typedef int net_recvbuf_cb(void *obj, Socket sock);
-typedef int net_recv_cb(void *obj, Socket sock, uint8_t *buf, size_t len);
-typedef int net_recvfrom_cb(void *obj, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr);
-typedef int net_send_cb(void *obj, Socket sock, const uint8_t *buf, size_t len);
-typedef int net_sendto_cb(void *obj, Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr);
-typedef Socket net_socket_cb(void *obj, int domain, int type, int proto);
-typedef int net_socket_nonblock_cb(void *obj, Socket sock, bool nonblock);
-typedef int net_getsockopt_cb(void *obj, Socket sock, int level, int optname, void *optval, size_t *optlen);
-typedef int net_setsockopt_cb(void *obj, Socket sock, int level, int optname, const void *optval, size_t optlen);
-typedef int net_getaddrinfo_cb(void *obj, const Memory *mem, const char *address, int family, int protocol, Network_Addr **addrs);
-typedef int net_freeaddrinfo_cb(void *obj, const Memory *mem, Network_Addr *addrs);
-
-/** @brief Functions wrapping POSIX network functions.
- *
- * Refer to POSIX man pages for documentation of what these functions are
- * expected to do when providing alternative Network implementations.
- */
-typedef struct Network_Funcs {
-    net_close_cb *close;
-    net_accept_cb *accept;
-    net_bind_cb *bind;
-    net_listen_cb *listen;
-    net_connect_cb *connect;
-    net_recvbuf_cb *recvbuf;
-    net_recv_cb *recv;
-    net_recvfrom_cb *recvfrom;
-    net_send_cb *send;
-    net_sendto_cb *sendto;
-    net_socket_cb *socket;
-    net_socket_nonblock_cb *socket_nonblock;
-    net_getsockopt_cb *getsockopt;
-    net_setsockopt_cb *setsockopt;
-    net_getaddrinfo_cb *getaddrinfo;
-    net_freeaddrinfo_cb *freeaddrinfo;
-} Network_Funcs;
-
-typedef struct Network {
-    const Network_Funcs *funcs;
-    void *obj;
-} Network;
-
-const Network *os_network(void);
-
 typedef struct Family {
     uint8_t value;
 } Family;
@@ -222,6 +162,8 @@ typedef struct IP_Port {
     uint16_t port;
 } IP_Port;
 
+typedef Tox_Network Network;
+
 non_null()
 Socket net_socket(const Network *ns, Family domain, int type, int protocol);
 
@@ -572,7 +514,7 @@ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool
  * @return true on success, false on failure.
  */
 non_null()
-bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port);
+bool bind_to_port(const Network *ns, const Memory *mem, Socket sock, Family family, uint16_t port);
 
 /** @brief Get the last networking error code.
  *
diff --git a/toxcore/network_test_util.cc b/toxcore/network_test_util.cc
index c8d2ff3e8a6..bd8f288b48f 100644
--- a/toxcore/network_test_util.cc
+++ b/toxcore/network_test_util.cc
@@ -6,84 +6,94 @@
 #include "mem.h"
 #include "network.h"
 #include "test_util.hh"
+#include "tox_network_impl.h"
 
-Network_Funcs const Network_Class::vtable = {
-    Method<net_close_cb, Network_Class>::invoke<&Network_Class::close>,
-    Method<net_accept_cb, Network_Class>::invoke<&Network_Class::accept>,
-    Method<net_bind_cb, Network_Class>::invoke<&Network_Class::bind>,
-    Method<net_listen_cb, Network_Class>::invoke<&Network_Class::listen>,
-    Method<net_connect_cb, Network_Class>::invoke<&Network_Class::connect>,
-    Method<net_recvbuf_cb, Network_Class>::invoke<&Network_Class::recvbuf>,
-    Method<net_recv_cb, Network_Class>::invoke<&Network_Class::recv>,
-    Method<net_recvfrom_cb, Network_Class>::invoke<&Network_Class::recvfrom>,
-    Method<net_send_cb, Network_Class>::invoke<&Network_Class::send>,
-    Method<net_sendto_cb, Network_Class>::invoke<&Network_Class::sendto>,
-    Method<net_socket_cb, Network_Class>::invoke<&Network_Class::socket>,
-    Method<net_socket_nonblock_cb, Network_Class>::invoke<&Network_Class::socket_nonblock>,
-    Method<net_getsockopt_cb, Network_Class>::invoke<&Network_Class::getsockopt>,
-    Method<net_setsockopt_cb, Network_Class>::invoke<&Network_Class::setsockopt>,
-    Method<net_getaddrinfo_cb, Network_Class>::invoke<&Network_Class::getaddrinfo>,
-    Method<net_freeaddrinfo_cb, Network_Class>::invoke<&Network_Class::freeaddrinfo>,
+Tox_Network_Funcs const Network_Class::vtable = {
+    Method<tox_network_close_cb, Network_Class>::invoke<&Network_Class::close>,
+    Method<tox_network_accept_cb, Network_Class>::invoke<&Network_Class::accept>,
+    Method<tox_network_bind_cb, Network_Class>::invoke<&Network_Class::bind>,
+    Method<tox_network_listen_cb, Network_Class>::invoke<&Network_Class::listen>,
+    Method<tox_network_connect_cb, Network_Class>::invoke<&Network_Class::connect>,
+    Method<tox_network_recvbuf_cb, Network_Class>::invoke<&Network_Class::recvbuf>,
+    Method<tox_network_recv_cb, Network_Class>::invoke<&Network_Class::recv>,
+    Method<tox_network_recvfrom_cb, Network_Class>::invoke<&Network_Class::recvfrom>,
+    Method<tox_network_send_cb, Network_Class>::invoke<&Network_Class::send>,
+    Method<tox_network_sendto_cb, Network_Class>::invoke<&Network_Class::sendto>,
+    Method<tox_network_socket_cb, Network_Class>::invoke<&Network_Class::socket>,
+    Method<tox_network_socket_nonblock_cb, Network_Class>::invoke<&Network_Class::socket_nonblock>,
+    Method<tox_network_getsockopt_cb, Network_Class>::invoke<&Network_Class::getsockopt>,
+    Method<tox_network_setsockopt_cb, Network_Class>::invoke<&Network_Class::setsockopt>,
+    Method<tox_network_getaddrinfo_cb, Network_Class>::invoke<&Network_Class::getaddrinfo>,
+    Method<tox_network_freeaddrinfo_cb, Network_Class>::invoke<&Network_Class::freeaddrinfo>,
 };
 
-int Test_Network::close(void *obj, Socket sock) { return net->funcs->close(net->obj, sock); }
-Socket Test_Network::accept(void *obj, Socket sock) { return net->funcs->accept(net->obj, sock); }
+int Test_Network::close(void *obj, Socket sock)
+{
+    return net->funcs->close_callback(net->user_data, sock);
+}
+Socket Test_Network::accept(void *obj, Socket sock)
+{
+    return net->funcs->accept_callback(net->user_data, sock);
+}
 int Test_Network::bind(void *obj, Socket sock, const Network_Addr *addr)
 {
-    return net->funcs->bind(net->obj, sock, addr);
+    return net->funcs->bind_callback(net->user_data, sock, addr);
 }
 int Test_Network::listen(void *obj, Socket sock, int backlog)
 {
-    return net->funcs->listen(net->obj, sock, backlog);
+    return net->funcs->listen_callback(net->user_data, sock, backlog);
 }
 int Test_Network::connect(void *obj, Socket sock, const Network_Addr *addr)
 {
-    return net->funcs->connect(net->obj, sock, addr);
+    return net->funcs->connect_callback(net->user_data, sock, addr);
+}
+int Test_Network::recvbuf(void *obj, Socket sock)
+{
+    return net->funcs->recvbuf_callback(net->user_data, sock);
 }
-int Test_Network::recvbuf(void *obj, Socket sock) { return net->funcs->recvbuf(net->obj, sock); }
 int Test_Network::recv(void *obj, Socket sock, uint8_t *buf, size_t len)
 {
-    return net->funcs->recv(net->obj, sock, buf, len);
+    return net->funcs->recv_callback(net->user_data, sock, buf, len);
 }
 int Test_Network::recvfrom(void *obj, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr)
 {
-    return net->funcs->recvfrom(net->obj, sock, buf, len, addr);
+    return net->funcs->recvfrom_callback(net->user_data, sock, buf, len, addr);
 }
 int Test_Network::send(void *obj, Socket sock, const uint8_t *buf, size_t len)
 {
-    return net->funcs->send(net->obj, sock, buf, len);
+    return net->funcs->send_callback(net->user_data, sock, buf, len);
 }
 int Test_Network::sendto(
     void *obj, Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr)
 {
-    return net->funcs->sendto(net->obj, sock, buf, len, addr);
+    return net->funcs->sendto_callback(net->user_data, sock, buf, len, addr);
 }
 Socket Test_Network::socket(void *obj, int domain, int type, int proto)
 {
-    return net->funcs->socket(net->obj, domain, type, proto);
+    return net->funcs->socket_callback(net->user_data, domain, type, proto);
 }
 int Test_Network::socket_nonblock(void *obj, Socket sock, bool nonblock)
 {
-    return net->funcs->socket_nonblock(net->obj, sock, nonblock);
+    return net->funcs->socket_nonblock_callback(net->user_data, sock, nonblock);
 }
 int Test_Network::getsockopt(
     void *obj, Socket sock, int level, int optname, void *optval, size_t *optlen)
 {
-    return net->funcs->getsockopt(net->obj, sock, level, optname, optval, optlen);
+    return net->funcs->getsockopt_callback(net->user_data, sock, level, optname, optval, optlen);
 }
 int Test_Network::setsockopt(
     void *obj, Socket sock, int level, int optname, const void *optval, size_t optlen)
 {
-    return net->funcs->setsockopt(net->obj, sock, level, optname, optval, optlen);
+    return net->funcs->setsockopt_callback(net->user_data, sock, level, optname, optval, optlen);
 }
 int Test_Network::getaddrinfo(void *obj, const Memory *mem, const char *address, int family,
     int protocol, Network_Addr **addrs)
 {
-    return net->funcs->getaddrinfo(net->obj, mem, address, family, protocol, addrs);
+    return net->funcs->getaddrinfo_callback(net->user_data, mem, address, family, protocol, addrs);
 }
 int Test_Network::freeaddrinfo(void *obj, const Memory *mem, Network_Addr *addrs)
 {
-    return net->funcs->freeaddrinfo(net->obj, mem, addrs);
+    return net->funcs->freeaddrinfo_callback(net->user_data, mem, addrs);
 }
 
 Network_Class::~Network_Class() = default;
diff --git a/toxcore/network_test_util.hh b/toxcore/network_test_util.hh
index be967d3d4f8..e205157e612 100644
--- a/toxcore/network_test_util.hh
+++ b/toxcore/network_test_util.hh
@@ -6,13 +6,15 @@
 #include "crypto_core.h"
 #include "mem.h"
 #include "network.h"
+#include "os_network.h"
 #include "test_util.hh"
+#include "tox_network_impl.h"
 
 struct Network_Class {
-    static Network_Funcs const vtable;
-    Network const self;
+    static Tox_Network_Funcs const vtable;
+    Tox_Network const self;
 
-    operator Network const *() const { return &self; }
+    operator Tox_Network const *() const { return &self; }
 
     Network_Class(Network_Class const &) = default;
     Network_Class()
@@ -21,22 +23,22 @@ struct Network_Class {
     }
 
     virtual ~Network_Class();
-    virtual net_close_cb close = 0;
-    virtual net_accept_cb accept = 0;
-    virtual net_bind_cb bind = 0;
-    virtual net_listen_cb listen = 0;
-    virtual net_connect_cb connect = 0;
-    virtual net_recvbuf_cb recvbuf = 0;
-    virtual net_recv_cb recv = 0;
-    virtual net_recvfrom_cb recvfrom = 0;
-    virtual net_send_cb send = 0;
-    virtual net_sendto_cb sendto = 0;
-    virtual net_socket_cb socket = 0;
-    virtual net_socket_nonblock_cb socket_nonblock = 0;
-    virtual net_getsockopt_cb getsockopt = 0;
-    virtual net_setsockopt_cb setsockopt = 0;
-    virtual net_getaddrinfo_cb getaddrinfo = 0;
-    virtual net_freeaddrinfo_cb freeaddrinfo = 0;
+    virtual tox_network_close_cb close = 0;
+    virtual tox_network_accept_cb accept = 0;
+    virtual tox_network_bind_cb bind = 0;
+    virtual tox_network_listen_cb listen = 0;
+    virtual tox_network_connect_cb connect = 0;
+    virtual tox_network_recvbuf_cb recvbuf = 0;
+    virtual tox_network_recv_cb recv = 0;
+    virtual tox_network_recvfrom_cb recvfrom = 0;
+    virtual tox_network_send_cb send = 0;
+    virtual tox_network_sendto_cb sendto = 0;
+    virtual tox_network_socket_cb socket = 0;
+    virtual tox_network_socket_nonblock_cb socket_nonblock = 0;
+    virtual tox_network_getsockopt_cb getsockopt = 0;
+    virtual tox_network_setsockopt_cb setsockopt = 0;
+    virtual tox_network_getaddrinfo_cb getaddrinfo = 0;
+    virtual tox_network_freeaddrinfo_cb freeaddrinfo = 0;
 };
 
 /**
@@ -44,7 +46,7 @@ struct Network_Class {
  * subclassed to override individual (or all) functions.
  */
 class Test_Network : public Network_Class {
-    const Network *net = REQUIRE_NOT_NULL(os_network());
+    const Tox_Network *net = REQUIRE_NOT_NULL(os_network());
 
     int close(void *obj, Socket sock) override;
     Socket accept(void *obj, Socket sock) override;
diff --git a/toxcore/os_log.c b/toxcore/os_log.c
new file mode 100644
index 00000000000..c218b1b19b0
--- /dev/null
+++ b/toxcore/os_log.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "os_log.h"
+
+#include <stdint.h>
+
+#include "attributes.h"
+#include "tox_log.h"
+#include "tox_log_impl.h"
+
+non_null()
+static void os_log_log(
+    void *self, Tox_Log_Level level,
+    const char *file, uint32_t line, const char *func,
+    const char *message)
+{
+    // Do nothing with the log message by default.
+    return;
+}
+
+static const Tox_Log_Funcs os_log_funcs = {
+    os_log_log,
+};
+
+const Tox_Log os_log_obj = {&os_log_funcs};
+
+const Tox_Log *os_log(void)
+{
+    return &os_log_obj;
+}
diff --git a/toxcore/os_log.h b/toxcore/os_log.h
new file mode 100644
index 00000000000..cc0e7f7ab09
--- /dev/null
+++ b/toxcore/os_log.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_OS_LOG_H
+#define C_TOXCORE_TOXCORE_OS_LOG_H
+
+#include "tox_log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const Tox_Log os_log_obj;
+
+const Tox_Log *os_log(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_OS_LOG_H */
diff --git a/toxcore/os_memory.c b/toxcore/os_memory.c
new file mode 100644
index 00000000000..0018c26d607
--- /dev/null
+++ b/toxcore/os_memory.c
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "os_memory.h"
+
+#include <stdlib.h>
+
+#include "attributes.h"
+#include "tox_memory.h"
+#include "tox_memory_impl.h"
+
+non_null()
+static void *os_malloc(void *self, uint32_t size)
+{
+    // cppcheck-suppress misra-c2012-21.3
+    return malloc(size);
+}
+
+non_null(1) nullable(2)
+static void *os_realloc(void *self, void *ptr, uint32_t size)
+{
+    // cppcheck-suppress misra-c2012-21.3
+    return realloc(ptr, size);
+}
+
+non_null(1) nullable(2)
+static void os_free(void *self, void *ptr)
+{
+    // cppcheck-suppress misra-c2012-21.3
+    free(ptr);
+}
+
+static const Tox_Memory_Funcs os_memory_funcs = {
+    os_malloc,
+    os_realloc,
+    os_free,
+};
+const Tox_Memory os_memory_obj = {&os_memory_funcs};
+
+const Tox_Memory *os_memory(void)
+{
+    return &os_memory_obj;
+}
diff --git a/toxcore/os_memory.h b/toxcore/os_memory.h
new file mode 100644
index 00000000000..41b71160538
--- /dev/null
+++ b/toxcore/os_memory.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_OS_MEMORY_H
+#define C_TOXCORE_TOXCORE_OS_MEMORY_H
+
+#include "tox_memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const Tox_Memory os_memory_obj;
+
+const Tox_Memory *os_memory(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_OS_MEMORY_H */
diff --git a/toxcore/os_network.c b/toxcore/os_network.c
new file mode 100644
index 00000000000..0a2d4b22bae
--- /dev/null
+++ b/toxcore/os_network.c
@@ -0,0 +1,409 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#ifdef __APPLE__
+#define _DARWIN_C_SOURCE
+#endif /* __APPLE__ */
+
+// For Solaris.
+#ifdef __sun
+#define __EXTENSIONS__ 1
+#endif /* __sun */
+
+// For Linux (and some BSDs).
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 700
+#endif /* _XOPEN_SOURCE */
+
+#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_WINXP
+#undef _WIN32_WINNT
+#define _WIN32_WINNT  0x501
+#endif /* _WIN32 */
+
+#if !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32))
+#define OS_WIN32
+#endif /* OS_WIN32 */
+
+#if defined(OS_WIN32) && !defined(WINVER)
+// Windows XP
+#define WINVER 0x0501
+#endif /* OS_WIN32 */
+
+#include "os_network.h"
+
+#ifdef OS_WIN32 // Put win32 includes here
+// The mingw32/64 Windows library warns about including winsock2.h after
+// windows.h even though with the above it's a valid thing to do. So, to make
+// mingw32 headers happy, we include winsock2.h first.
+#include <winsock2.h>
+// Comment line here to avoid reordering by source code formatters.
+#include <windows.h>
+#include <ws2tcpip.h>
+#endif /* OS_WIN32 */
+
+#if !defined(OS_WIN32)
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __sun
+#include <stropts.h>
+#include <sys/filio.h>
+#endif /* __sun */
+
+#else
+#ifndef IPV6_V6ONLY
+#define IPV6_V6ONLY 27
+#endif /* IPV6_V6ONLY */
+#endif /* OS_WIN32 */
+
+#include <assert.h>
+#include <string.h>  // memcpy
+
+#include "attributes.h"
+#include "ccompat.h"
+#include "os_network_impl.h"
+#include "mem.h"
+#include "tox_memory.h"
+#include "tox_network.h"
+#include "tox_network_impl.h"
+
+// Disable MSG_NOSIGNAL on systems not supporting it, e.g. Windows, FreeBSD
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL 0
+#endif /* MSG_NOSIGNAL */
+
+non_null()
+static int os_close(void *self, Socket sock)
+{
+#if defined(OS_WIN32)
+    return closesocket(net_socket_to_native(sock));
+#else  // !OS_WIN32
+    return close(net_socket_to_native(sock));
+#endif /* OS_WIN32 */
+}
+
+struct Network_Addr {
+    struct sockaddr_storage addr;
+    size_t size;
+};
+
+Network_Addr *net_addr_new(const void *data, size_t size, const Tox_Memory *mem)
+{
+    Network_Addr *addr = (Network_Addr *)tox_memory_malloc(mem, sizeof(Network_Addr));
+
+    if (addr == nullptr) {
+        return nullptr;
+    }
+
+    if (data != nullptr) {
+        net_addr_set(addr, data, size);
+    } else {
+        addr->size = sizeof(struct sockaddr_storage);
+    }
+
+    return addr;
+}
+
+void net_addr_free(Network_Addr *addr, const Tox_Memory *mem)
+{
+    tox_memory_dealloc(mem, addr);
+}
+
+void net_addr_set(Network_Addr *addr, const void *data, size_t size)
+{
+    assert(size <= sizeof(struct sockaddr_storage));
+    memcpy(&addr->addr, data, size);
+    addr->size = size;
+}
+
+void *net_addr_mut_addr(Network_Addr *addr)
+{
+    return &addr->addr;
+}
+
+const void *net_addr_get_addr(const Network_Addr *addr)
+{
+    return &addr->addr;
+}
+
+void net_addr_set_size(Network_Addr *addr, size_t size)
+{
+    addr->size = size;
+}
+
+size_t net_addr_get_size(const Network_Addr *addr)
+{
+    return addr->size;
+}
+
+bool net_addr_is_ipv4(const Network_Addr *addr)
+{
+    return addr->addr.ss_family == AF_INET;
+}
+
+bool net_addr_is_ipv6(const Network_Addr *addr)
+{
+    return addr->addr.ss_family == AF_INET6;
+}
+
+uint16_t net_addr_get_port(const Network_Addr *addr)
+{
+    const int family = addr->addr.ss_family;
+    if (family == AF_INET6) {
+        const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)&addr->addr;
+        return addr6->sin6_port;
+    } else {
+        assert(family == AF_INET);
+        const struct sockaddr_in *addr4 = (const struct sockaddr_in *)&addr->addr;
+        return addr4->sin_port;
+    }
+}
+
+int net_addr_get_family(const Network_Addr *addr)
+{
+    return addr->addr.ss_family;
+}
+
+const Network_Addr *net_addrs_get_addr(const Network_Addr *addr, uint32_t index)
+{
+    return &addr[index];
+}
+
+
+non_null()
+static Socket os_accept(void *self, Socket sock)
+{
+    return net_socket_from_native(accept(net_socket_to_native(sock), nullptr, nullptr));
+}
+
+non_null()
+static int os_bind(void *self, Socket sock, const Network_Addr *addr)
+{
+    return bind(net_socket_to_native(sock), (const struct sockaddr *)net_addr_get_addr(addr), net_addr_get_size(addr));
+}
+
+non_null()
+static int os_listen(void *self, Socket sock, int backlog)
+{
+    return listen(net_socket_to_native(sock), backlog);
+}
+
+non_null()
+static int os_connect(void *self, Socket sock, const Network_Addr *addr)
+{
+    return connect(net_socket_to_native(sock), (const struct sockaddr *)net_addr_get_addr(addr), net_addr_get_size(addr));
+}
+
+non_null()
+static int os_recvbuf(void *self, Socket sock)
+{
+#ifdef OS_WIN32
+    u_long count = 0;
+    ioctlsocket(net_socket_to_native(sock), FIONREAD, &count);
+#else
+    int count = 0;
+    ioctl(net_socket_to_native(sock), FIONREAD, &count);
+#endif /* OS_WIN32 */
+
+    return count;
+}
+
+non_null()
+static int os_recv(void *self, Socket sock, uint8_t *buf, size_t len)
+{
+    return recv(net_socket_to_native(sock), (char *)buf, len, MSG_NOSIGNAL);
+}
+
+non_null()
+static int os_send(void *self, Socket sock, const uint8_t *buf, size_t len)
+{
+    return send(net_socket_to_native(sock), (const char *)buf, len, MSG_NOSIGNAL);
+}
+
+non_null()
+static int os_sendto(void *self, Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr)
+{
+    return sendto(net_socket_to_native(sock), (const char *)buf, len, 0, (const struct sockaddr *)&addr->addr, addr->size);
+}
+
+non_null()
+static int os_recvfrom(void *self, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr)
+{
+    socklen_t size = addr->size;
+    const int ret = recvfrom(net_socket_to_native(sock), (char *)buf, len, 0, (struct sockaddr *)&addr->addr, &size);
+    addr->size = size;
+    return ret;
+}
+
+non_null()
+static Socket os_socket(void *self, int domain, int type, int proto)
+{
+    return net_socket_from_native(socket(domain, type, proto));
+}
+
+non_null()
+static int os_socket_nonblock(void *self, Socket sock, bool nonblock)
+{
+#ifdef OS_WIN32
+    u_long mode = nonblock ? 1 : 0;
+    return ioctlsocket(net_socket_to_native(sock), FIONBIO, &mode);
+#else
+    return fcntl(net_socket_to_native(sock), F_SETFL, O_NONBLOCK, nonblock ? 1 : 0);
+#endif /* OS_WIN32 */
+}
+
+non_null()
+static int os_getsockopt(void *self, Socket sock, int level, int optname, void *optval, size_t *optlen)
+{
+    char *optval_bytes = (char *)optval;
+    socklen_t len = *optlen;
+    const int ret = getsockopt(net_socket_to_native(sock), level, optname, optval_bytes, &len);
+    *optlen = len;
+    return ret;
+}
+
+non_null()
+static int os_setsockopt(void *self, Socket sock, int level, int optname, const void *optval, size_t optlen)
+{
+    const char *optval_bytes = (const char *)optval;
+    return setsockopt(net_socket_to_native(sock), level, optname, optval_bytes, optlen);
+}
+
+// sets and fills an array of addrs for address
+// returns the number of entries in addrs
+non_null()
+static int os_getaddrinfo(void *obj, const Tox_Memory *mem, const char *address, int family, int sock_type, Network_Addr **addrs)
+{
+    assert(addrs != nullptr);
+
+    struct addrinfo hints = {0};
+    hints.ai_family = family;
+
+
+    // different platforms favour a different field
+    // hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses.
+    hints.ai_socktype = sock_type;
+    // hints.ai_protocol = protocol;
+
+    struct addrinfo *infos = nullptr;
+
+    const int rc = getaddrinfo(address, nullptr, &hints, &infos);
+
+    // Lookup failed.
+    if (rc != 0) {
+        // TODO(Green-Sky): log error
+        return 0;
+    }
+
+    const int32_t max_count = INT32_MAX / sizeof(Network_Addr);
+
+    // we count number of "valid" results
+    int result = 0;
+    for (struct addrinfo *walker = infos; walker != nullptr && result < max_count; walker = walker->ai_next) {
+        if (walker->ai_family == family || family == AF_UNSPEC) {
+            ++result;
+        }
+
+        // do we need to check socktype/protocol?
+    }
+
+    assert(max_count >= result);
+
+    Network_Addr *tmp_addrs = (Network_Addr *)mem_valloc(mem, result, sizeof(Network_Addr));
+    if (tmp_addrs == nullptr) {
+        freeaddrinfo(infos);
+        return 0;
+    }
+
+    // now we fill in
+    int i = 0;
+    for (struct addrinfo *walker = infos; walker != nullptr; walker = walker->ai_next) {
+        if (walker->ai_family == family || family == AF_UNSPEC) {
+            tmp_addrs[i].size = sizeof(struct sockaddr_storage);
+            tmp_addrs[i].addr.ss_family = walker->ai_family;
+
+            // according to spec, storage is supposed to be large enough (and source shows they are)
+            // storage is 128 bytes
+            assert(walker->ai_addrlen <= tmp_addrs[i].size);
+
+            memcpy(&tmp_addrs[i].addr, walker->ai_addr, walker->ai_addrlen);
+            tmp_addrs[i].size = walker->ai_addrlen;
+
+            ++i;
+        }
+    }
+
+    assert(i == result);
+
+    freeaddrinfo(infos);
+
+    *addrs = tmp_addrs;
+
+    // number of entries in addrs
+    return result;
+}
+
+non_null()
+static int os_freeaddrinfo(void *obj, const Tox_Memory *mem, Network_Addr *addrs)
+{
+    if (addrs == nullptr) {
+        return 0;
+    }
+
+    mem_delete(mem, addrs);
+
+    return 0;
+}
+
+static const Tox_Network_Funcs os_network_funcs = {
+    os_close,
+    os_accept,
+    os_bind,
+    os_listen,
+    os_connect,
+    os_recvbuf,
+    os_recv,
+    os_recvfrom,
+    os_send,
+    os_sendto,
+    os_socket,
+    os_socket_nonblock,
+    os_getsockopt,
+    os_setsockopt,
+    os_getaddrinfo,
+    os_freeaddrinfo,
+};
+const Tox_Network os_network_obj = {&os_network_funcs};
+
+#if 0
+/* TODO(iphydf): Call this from functions that use `os_network()`. */
+void os_network_deinit(const Network *ns)
+{
+#ifdef OS_WIN32
+    WSACleanup();
+#endif /* OS_WIN32 */
+}
+#endif /* 0 */
+
+const Tox_Network *os_network(void)
+{
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if ((true)) {
+        return nullptr;
+    }
+#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+#ifdef OS_WIN32
+    WSADATA wsaData;
+
+    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
+        return nullptr;
+    }
+#endif /* OS_WIN32 */
+    return &os_network_obj;
+}
diff --git a/toxcore/os_network.h b/toxcore/os_network.h
new file mode 100644
index 00000000000..3bf383e2e43
--- /dev/null
+++ b/toxcore/os_network.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_OS_NETWORK_H
+#define C_TOXCORE_TOXCORE_OS_NETWORK_H
+
+#include "tox_network.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const Tox_Network os_network_obj;
+
+const Tox_Network *os_network(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_OS_NETWORK_H */
diff --git a/toxcore/os_network_impl.h b/toxcore/os_network_impl.h
new file mode 100644
index 00000000000..d14286f6ca2
--- /dev/null
+++ b/toxcore/os_network_impl.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_OS_NETWORK_IMPL_H
+#define C_TOXCORE_TOXCORE_OS_NETWORK_IMPL_H
+
+#include "attributes.h"
+#include "tox_memory.h"
+#include "tox_network.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+non_null(3) nullable(1) Network_Addr *net_addr_new(const void *data, size_t size, const Tox_Memory *mem);
+non_null(2) nullable(1) void net_addr_free(Network_Addr *addr, const Tox_Memory *mem);
+
+non_null() void net_addr_set(Network_Addr *addr, const void *data, size_t size);
+
+non_null() void *net_addr_mut_addr(Network_Addr *addr);
+non_null() const void *net_addr_get_addr(const Network_Addr *addr);
+
+non_null() void net_addr_set_size(Network_Addr *addr, size_t size);
+non_null() size_t net_addr_get_size(const Network_Addr *addr);
+
+non_null() bool net_addr_is_ipv4(const Network_Addr *addr);
+non_null() bool net_addr_is_ipv6(const Network_Addr *addr);
+
+non_null() uint16_t net_addr_get_port(const Network_Addr *addr);
+non_null() int net_addr_get_family(const Network_Addr *addr);
+
+non_null() const Network_Addr *net_addrs_get_addr(const Network_Addr *addrs, uint32_t index);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_OS_NETWORK_IMPL_H */
diff --git a/toxcore/os_random.c b/toxcore/os_random.c
new file mode 100644
index 00000000000..65027c06fa9
--- /dev/null
+++ b/toxcore/os_random.c
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "os_random.h"
+
+#include <sodium.h>
+
+#include "attributes.h"
+#include "ccompat.h"
+#include "tox_random.h"
+#include "tox_random_impl.h"
+
+non_null()
+static void os_random_bytes(void *self, uint8_t *bytes, uint32_t length)
+{
+    randombytes(bytes, length);
+}
+
+non_null()
+static uint32_t os_random_uniform(void *self, uint32_t upper_bound)
+{
+    return randombytes_uniform(upper_bound);
+}
+
+static const Tox_Random_Funcs os_random_funcs = {
+    os_random_bytes,
+    os_random_uniform,
+};
+
+const Tox_Random os_random_obj = {&os_random_funcs};
+
+const Tox_Random *os_random(void)
+{
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if ((true)) {
+        return nullptr;
+    }
+#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+    // It is safe to call this function more than once and from different
+    // threads -- subsequent calls won't have any effects.
+    if (sodium_init() == -1) {
+        return nullptr;
+    }
+    return &os_random_obj;
+}
diff --git a/toxcore/os_random.h b/toxcore/os_random.h
new file mode 100644
index 00000000000..30047808373
--- /dev/null
+++ b/toxcore/os_random.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_OS_RANDOM_H
+#define C_TOXCORE_TOXCORE_OS_RANDOM_H
+
+#include "tox_random.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const Tox_Random os_random_obj;
+
+/** @brief System random number generator.
+ *
+ * Uses libsodium's CSPRNG (on Linux, `/dev/urandom`).
+ */
+const Tox_Random *os_random(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_OS_RANDOM_H */
diff --git a/toxcore/os_system.c b/toxcore/os_system.c
new file mode 100644
index 00000000000..9b1de312733
--- /dev/null
+++ b/toxcore/os_system.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "os_system.h"
+
+#include "ccompat.h"
+#include "os_log.h"
+#include "os_memory.h"
+#include "os_network.h"
+#include "os_random.h"
+#include "tox_system.h"
+#include "tox_system_impl.h"
+
+static const Tox_System os_system_obj = {
+    &os_log_obj,
+    &os_memory_obj,
+    &os_network_obj,
+    &os_random_obj,
+    nullptr,
+};
+
+const Tox_System *os_system(void)
+{
+    const Tox_System *sys = &os_system_obj;
+
+    if (sys->log != os_log()
+            || sys->mem != os_memory()
+            || sys->ns != os_network()
+            || sys->rng != os_random()) {
+        return nullptr;
+    }
+
+    return sys;
+}
diff --git a/toxcore/os_system.h b/toxcore/os_system.h
new file mode 100644
index 00000000000..e3a68020e37
--- /dev/null
+++ b/toxcore/os_system.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_OS_SYSTEM_H
+#define C_TOXCORE_TOXCORE_OS_SYSTEM_H
+
+#include "tox_system.h"
+#include "tox_system_impl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Default operating-system-backed `Tox_System`.
+ *
+ * Only `Tox_Time` does not have a subsystem here, and instead is created in
+ * `mono_time`.
+ *
+ * This function, and by extension all the subsystem functions, does not
+ * allocate any dynamic memory.
+ */
+const Tox_System *os_system(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_OS_SYSTEM_H */
diff --git a/toxcore/os_time.c b/toxcore/os_time.c
new file mode 100644
index 00000000000..ac0a121da37
--- /dev/null
+++ b/toxcore/os_time.c
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600
+#endif /* _XOPEN_SOURCE */
+
+#if !defined(OS_WIN32) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32))
+#define OS_WIN32
+#endif /* WIN32 */
+
+#include "os_time.h"
+
+#ifdef OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif /* OS_WIN32 */
+
+#ifdef __APPLE__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif /* __APPLE__ */
+
+#ifndef OS_WIN32
+#include <sys/time.h>
+#endif /* OS_WIN32 */
+
+#include <stdint.h>
+#include <time.h>
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+#include <assert.h>
+#include "ccompat.h"
+#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+
+#include "attributes.h"
+#include "tox_time.h"
+#include "tox_time_impl.h"
+
+static uint64_t timespec_to_u64(struct timespec clock_mono)
+{
+    return UINT64_C(1000) * clock_mono.tv_sec + (clock_mono.tv_nsec / UINT64_C(1000000));
+}
+
+#ifdef OS_WIN32
+non_null()
+static uint64_t current_time_monotonic_default(void *user_data)
+{
+    LARGE_INTEGER freq;
+    LARGE_INTEGER count;
+    if (!QueryPerformanceFrequency(&freq)) {
+        return 0;
+    }
+    if (!QueryPerformanceCounter(&count)) {
+        return 0;
+    }
+    struct timespec sp = {0};
+    sp.tv_sec = count.QuadPart / freq.QuadPart;
+    if (freq.QuadPart < 1000000000) {
+        sp.tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart;
+    } else {
+        sp.tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart));
+    }
+    return timespec_to_u64(sp);
+}
+#else
+#ifdef __APPLE__
+non_null()
+static uint64_t current_time_monotonic_default(void *user_data)
+{
+    struct timespec clock_mono;
+    clock_serv_t muhclock;
+    mach_timespec_t machtime;
+
+    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &muhclock);
+    clock_get_time(muhclock, &machtime);
+    mach_port_deallocate(mach_task_self(), muhclock);
+
+    clock_mono.tv_sec = machtime.tv_sec;
+    clock_mono.tv_nsec = machtime.tv_nsec;
+    return timespec_to_u64(clock_mono);
+}
+#else // !__APPLE__
+non_null()
+static uint64_t current_time_monotonic_default(void *user_data)
+{
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    // This assert should always fail. If it does, the fuzzing harness didn't
+    // override the mono time callback.
+    assert(user_data == nullptr);
+#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+    struct timespec clock_mono;
+    clock_gettime(CLOCK_MONOTONIC, &clock_mono);
+    return timespec_to_u64(clock_mono);
+}
+#endif /* !__APPLE__ */
+#endif /* !OS_WIN32 */
+
+static const Tox_Time_Funcs os_time_funcs = {
+    current_time_monotonic_default,
+};
+
+const Tox_Time os_time_obj = {&os_time_funcs};
+
+const Tox_Time *os_time(void)
+{
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if ((true)) {
+        return nullptr;
+    }
+#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+    return &os_time_obj;
+}
diff --git a/toxcore/os_time.h b/toxcore/os_time.h
new file mode 100644
index 00000000000..80a1c50caaa
--- /dev/null
+++ b/toxcore/os_time.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_OS_TIME_H
+#define C_TOXCORE_TOXCORE_OS_TIME_H
+
+#include "tox_time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const Tox_Time os_time_obj;
+
+const Tox_Time *os_time(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_OS_TIME_H */
diff --git a/toxcore/ping_array_test.cc b/toxcore/ping_array_test.cc
index cc9d0bfc8b8..d3342ac5f2a 100644
--- a/toxcore/ping_array_test.cc
+++ b/toxcore/ping_array_test.cc
@@ -7,6 +7,7 @@
 #include "crypto_core_test_util.hh"
 #include "mem_test_util.hh"
 #include "mono_time.h"
+#include "os_random.h"
 
 namespace {
 
@@ -65,7 +66,7 @@ TEST(PingArray, StoredDataCanBeRetrieved)
     Test_Random rng;
 
     Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1));
-    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem);
+    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr), mem);
     ASSERT_NE(mono_time, nullptr);
 
     uint64_t const ping_id = ping_array_add(
@@ -83,7 +84,7 @@ TEST(PingArray, RetrievingDataWithTooSmallOutputBufferHasNoEffect)
     Test_Random rng;
 
     Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1));
-    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem);
+    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr), mem);
     ASSERT_NE(mono_time, nullptr);
 
     uint64_t const ping_id = ping_array_add(
@@ -105,7 +106,7 @@ TEST(PingArray, ZeroLengthDataCanBeAdded)
     Test_Random rng;
 
     Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1));
-    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem);
+    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr), mem);
     ASSERT_NE(mono_time, nullptr);
 
     uint8_t c = 0;
@@ -120,7 +121,7 @@ TEST(PingArray, PingId0IsInvalid)
     Test_Memory mem;
 
     Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1));
-    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem);
+    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr), mem);
     ASSERT_NE(mono_time, nullptr);
 
     uint8_t c = 0;
@@ -134,7 +135,7 @@ TEST(PingArray, DataCanOnlyBeRetrievedOnce)
     Test_Random rng;
 
     Ping_Array_Ptr const arr(ping_array_new(mem, 2, 1));
-    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem);
+    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr), mem);
     ASSERT_NE(mono_time, nullptr);
 
     uint8_t c = 0;
@@ -151,7 +152,7 @@ TEST(PingArray, PingIdMustMatchOnCheck)
     Test_Random rng;
 
     Ping_Array_Ptr const arr(ping_array_new(mem, 1, 1));
-    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr, nullptr), mem);
+    Mono_Time_Ptr const mono_time(mono_time_new(mem, nullptr), mem);
     ASSERT_NE(mono_time, nullptr);
 
     uint8_t c = 0;
diff --git a/toxcore/sort_test_util.hh b/toxcore/sort_test_util.hh
index 52b2aeb54e6..3e8a2c83891 100644
--- a/toxcore/sort_test_util.hh
+++ b/toxcore/sort_test_util.hh
@@ -9,7 +9,7 @@
 
 #include "sort.h"
 
-struct Memory;
+struct Tox_Memory;
 
 template <typename T>
 constexpr Sort_Funcs sort_funcs()
@@ -41,7 +41,7 @@ constexpr Sort_Funcs sort_funcs()
 
 // A realistic test case where we have a struct with some stuff and an expensive value we compare.
 struct Some_Type {
-    const Memory *mem;
+    const Tox_Memory *mem;
     std::array<uint32_t, 8> compare_value;
     const char *name;
 
diff --git a/toxcore/tox.c b/toxcore/tox.c
index 7349928e4d7..3703550a09f 100644
--- a/toxcore/tox.c
+++ b/toxcore/tox.c
@@ -13,8 +13,10 @@
 #include "tox.h"
 
 #include <assert.h>
+#include <pthread.h>
 #include <string.h>
 
+#include "../toxencryptsave/defines.h"
 #include "DHT.h"
 #include "Messenger.h"
 #include "TCP_client.h"
@@ -31,13 +33,14 @@
 #include "net_crypto.h"
 #include "network.h"
 #include "onion_client.h"
+#include "os_system.h"
 #include "state.h"
+#include "tox_impl.h"
+#include "tox_log.h"
 #include "tox_private.h"
-#include "tox_struct.h" // IWYU pragma: keep
+#include "tox_system.h"
 #include "util.h"
 
-#include "../toxencryptsave/defines.h"
-
 #define SET_ERROR_PARAMETER(param, x) \
     do {                              \
         if (param != nullptr) {       \
@@ -72,6 +75,36 @@ static_assert(TOX_GROUP_MAX_MESSAGE_LENGTH == GROUP_MAX_MESSAGE_LENGTH,
 static_assert(TOX_MAX_CUSTOM_PACKET_SIZE == MAX_GC_CUSTOM_LOSSLESS_PACKET_SIZE,
               "TOX_MAX_CUSTOM_PACKET_SIZE is assumed to be equal to MAX_GC_CUSTOM_LOSSLESS_PACKET_SIZE");
 
+struct Tox_Mutex {
+    pthread_mutex_t underlying;
+};
+
+void tox_lock(const Tox *tox)
+{
+    if (tox->mutex != nullptr) {
+        pthread_mutex_lock(&tox->mutex->underlying);
+    }
+}
+
+void tox_unlock(const Tox *tox)
+{
+    if (tox->mutex != nullptr) {
+        pthread_mutex_unlock(&tox->mutex->underlying);
+    }
+}
+
+non_null()
+static void tox_mutex_init(Tox_Mutex *mutex)
+{
+    pthread_mutex_init(&mutex->underlying, nullptr);
+}
+
+non_null()
+static void tox_mutex_destroy(Tox_Mutex *mutex)
+{
+    pthread_mutex_destroy(&mutex->underlying);
+}
+
 struct Tox_Userdata {
     Tox *tox;
     void *user_data;
@@ -712,10 +745,9 @@ static int tox_load(Tox *tox, const uint8_t *data, uint32_t length)
                       length - cookie_len, STATE_COOKIE_TYPE);
 }
 
-nullable(1, 2, 3)
-static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error, const Tox_System *sys)
+Tox *tox_new(const Tox_Options *options, Tox_Err_New *error)
 {
-    struct Tox_Options *default_options = nullptr;
+    Tox_Options *default_options = nullptr;
 
     if (options == nullptr) {
         Tox_Err_Options_New err;
@@ -734,16 +766,16 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
         }
     }
 
-    const struct Tox_Options *const opts = options != nullptr ? options : default_options;
+    const Tox_Options *const opts = options != nullptr ? options : default_options;
     assert(opts != nullptr);
 
-    const Tox_System default_system = tox_default_system();
+    const Tox_System *sys = tox_options_get_operating_system(opts);
 
     if (sys == nullptr) {
-        sys = &default_system;
+        sys = os_system();
     }
 
-    if (sys->rng == nullptr || sys->ns == nullptr || sys->mem == nullptr) {
+    if (sys == nullptr || sys->rng == nullptr || sys->ns == nullptr || sys->mem == nullptr) {
         // TODO(iphydf): Not quite right, but similar.
         SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
         tox_options_free(default_options);
@@ -871,7 +903,7 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
         m_options.proxy_info.ip_port.port = net_htons(tox_options_get_proxy_port(opts));
     }
 
-    tox->mono_time = mono_time_new(tox->sys.mem, sys->mono_time_callback, sys->mono_time_user_data);
+    tox->mono_time = mono_time_new(tox->sys.mem, sys->tm);
 
     if (tox->mono_time == nullptr) {
         SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
@@ -881,7 +913,7 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
     }
 
     if (tox_options_get_experimental_thread_safety(opts)) {
-        pthread_mutex_t *mutex = (pthread_mutex_t *)mem_alloc(sys->mem, sizeof(pthread_mutex_t));
+        Tox_Mutex *mutex = (Tox_Mutex *)mem_alloc(sys->mem, sizeof(Tox_Mutex));
 
         if (mutex == nullptr) {
             SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC);
@@ -890,8 +922,7 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
             return nullptr;
         }
 
-        pthread_mutex_init(mutex, nullptr);
-
+        tox_mutex_init(mutex);
         tox->mutex = mutex;
     } else {
         tox->mutex = nullptr;
@@ -920,7 +951,7 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
         tox_unlock(tox);
 
         if (tox->mutex != nullptr) {
-            pthread_mutex_destroy(tox->mutex);
+            tox_mutex_destroy(tox->mutex);
         }
 
         mem_delete(sys->mem, tox->mutex);
@@ -938,7 +969,7 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
         tox_unlock(tox);
 
         if (tox->mutex != nullptr) {
-            pthread_mutex_destroy(tox->mutex);
+            tox_mutex_destroy(tox->mutex);
         }
 
         mem_delete(sys->mem, tox->mutex);
@@ -958,7 +989,7 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
         tox_unlock(tox);
 
         if (tox->mutex != nullptr) {
-            pthread_mutex_destroy(tox->mutex);
+            tox_mutex_destroy(tox->mutex);
         }
 
         mem_delete(sys->mem, tox->mutex);
@@ -1023,37 +1054,6 @@ static Tox *tox_new_system(const struct Tox_Options *options, Tox_Err_New *error
     return tox;
 }
 
-Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
-{
-    return tox_new_system(options, error, nullptr);
-}
-
-Tox *tox_new_testing(const Tox_Options *options, Tox_Err_New *error, const Tox_Options_Testing *testing, Tox_Err_New_Testing *testing_error)
-{
-    if (testing == nullptr) {
-        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_NULL);
-        SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_NULL);
-        return nullptr;
-    }
-
-    if (testing->operating_system == nullptr) {
-        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_NULL);
-        SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_NULL);
-        return nullptr;
-    }
-
-    const Tox_System *sys = testing->operating_system;
-
-    if (sys->rng == nullptr || sys->ns == nullptr || sys->mem == nullptr) {
-        SET_ERROR_PARAMETER(error, TOX_ERR_NEW_NULL);
-        SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_NULL);
-        return nullptr;
-    }
-
-    SET_ERROR_PARAMETER(testing_error, TOX_ERR_NEW_TESTING_OK);
-    return tox_new_system(options, error, sys);
-}
-
 void tox_kill(Tox *tox)
 {
     if (tox == nullptr) {
@@ -1068,7 +1068,7 @@ void tox_kill(Tox *tox)
     tox_unlock(tox);
 
     if (tox->mutex != nullptr) {
-        pthread_mutex_destroy(tox->mutex);
+        tox_mutex_destroy(tox->mutex);
         mem_delete(tox->sys.mem, tox->mutex);
     }
 
diff --git a/toxcore/tox.h b/toxcore/tox.h
index dddaf16fee5..171b82fabf4 100644
--- a/toxcore/tox.h
+++ b/toxcore/tox.h
@@ -102,6 +102,10 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "tox_attributes.h"
+#include "tox_log_level.h"
+#include "tox_options.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -393,439 +397,6 @@ const char *tox_message_type_to_string(Tox_Message_Type value);
 
 /** @} */
 
-/** @{
- * @name Startup options
- */
-
-/**
- * @brief Type of proxy used to connect to TCP relays.
- */
-typedef enum Tox_Proxy_Type {
-
-    /**
-     * Don't use a proxy.
-     */
-    TOX_PROXY_TYPE_NONE,
-
-    /**
-     * HTTP proxy using CONNECT.
-     */
-    TOX_PROXY_TYPE_HTTP,
-
-    /**
-     * SOCKS proxy for simple socket pipes.
-     */
-    TOX_PROXY_TYPE_SOCKS5,
-
-} Tox_Proxy_Type;
-
-const char *tox_proxy_type_to_string(Tox_Proxy_Type value);
-
-/**
- * @brief Type of savedata to create the Tox instance from.
- */
-typedef enum Tox_Savedata_Type {
-
-    /**
-     * No savedata.
-     */
-    TOX_SAVEDATA_TYPE_NONE,
-
-    /**
-     * Savedata is one that was obtained from tox_get_savedata.
-     */
-    TOX_SAVEDATA_TYPE_TOX_SAVE,
-
-    /**
-     * Savedata is a secret key of length TOX_SECRET_KEY_SIZE.
-     */
-    TOX_SAVEDATA_TYPE_SECRET_KEY,
-
-} Tox_Savedata_Type;
-
-const char *tox_savedata_type_to_string(Tox_Savedata_Type value);
-
-/**
- * @brief Severity level of log messages.
- */
-typedef enum Tox_Log_Level {
-
-    /**
-     * Very detailed traces including all network activity.
-     */
-    TOX_LOG_LEVEL_TRACE,
-
-    /**
-     * Debug messages such as which port we bind to.
-     */
-    TOX_LOG_LEVEL_DEBUG,
-
-    /**
-     * Informational log messages such as video call status changes.
-     */
-    TOX_LOG_LEVEL_INFO,
-
-    /**
-     * Warnings about internal inconsistency or logic errors.
-     */
-    TOX_LOG_LEVEL_WARNING,
-
-    /**
-     * Severe unexpected errors caused by external or internal inconsistency.
-     */
-    TOX_LOG_LEVEL_ERROR,
-
-} Tox_Log_Level;
-
-const char *tox_log_level_to_string(Tox_Log_Level value);
-
-/**
- * @brief This event is triggered when Tox logs an internal message.
- *
- * This is mostly useful for debugging. This callback can be called from any
- * function, not just tox_iterate. This means the user data lifetime must at
- * least extend between registering and unregistering it or tox_kill.
- *
- * Other toxcore modules such as toxav may concurrently call this callback at
- * any time. Thus, user code must make sure it is equipped to handle concurrent
- * execution, e.g. by employing appropriate mutex locking.
- *
- * When using the experimental_thread_safety option, no Tox API functions can
- * be called from within the log callback.
- *
- * @param level The severity of the log message.
- * @param file The source file from which the message originated.
- * @param line The source line from which the message originated.
- * @param func The function from which the message originated.
- * @param message The log message.
- * @param user_data The user data pointer passed to tox_new in options.
- */
-typedef void tox_log_cb(Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
-                        const char *message, void *user_data);
-
-/**
- * @brief This struct contains all the startup options for Tox.
- *
- * You must tox_options_new to allocate an object of this type.
- *
- * WARNING: Although this struct happens to be visible in the API, it is
- * effectively private. Do not allocate this yourself or access members
- * directly, as it *will* break binary compatibility frequently.
- *
- * @deprecated The memory layout of this struct (size, alignment, and field
- *   order) is not part of the ABI. To remain compatible, prefer to use
- *   tox_options_new to allocate the object and accessor functions to set the
- *   members. The struct will become opaque (i.e. the definition will become
- *   private) in v0.3.0.
- */
-typedef struct Tox_Options Tox_Options;
-#ifndef TOX_HIDE_DEPRECATED
-struct Tox_Options {
-
-    /**
-     * The type of socket to create.
-     *
-     * If this is set to false, an IPv4 socket is created, which subsequently
-     * only allows IPv4 communication.
-     * If it is set to true, an IPv6 socket is created, allowing both IPv4 and
-     * IPv6 communication.
-     */
-    bool ipv6_enabled;
-
-    /**
-     * Enable the use of UDP communication when available.
-     *
-     * Setting this to false will force Tox to use TCP only. Communications will
-     * need to be relayed through a TCP relay node, potentially slowing them
-     * down.
-     *
-     * If a proxy is enabled, UDP will be disabled if either the Tox library or
-     * the proxy don't support proxying UDP messages.
-     */
-    bool udp_enabled;
-
-    /**
-     * Enable local network peer discovery.
-     *
-     * Disabling this will cause Tox to not look for peers on the local network.
-     */
-    bool local_discovery_enabled;
-
-    /**
-     * Enable storing DHT announcements and forwarding corresponding requests.
-     *
-     * Disabling this will cause Tox to ignore the relevant packets.
-     */
-    bool dht_announcements_enabled;
-
-    /**
-     * Pass communications through a proxy.
-     */
-    Tox_Proxy_Type proxy_type;
-
-    /**
-     * The IP address or DNS name of the proxy to be used.
-     *
-     * If used, this must be non-NULL and be a valid DNS name. The name must not
-     * exceed TOX_MAX_HOSTNAME_LENGTH characters, and be in a NUL-terminated C
-     * string format (TOX_MAX_HOSTNAME_LENGTH includes the NUL byte).
-     *
-     * This member is ignored (it can be NULL) if proxy_type is
-     * TOX_PROXY_TYPE_NONE.
-     *
-     * The data pointed at by this member is owned by the user, so must
-     * outlive the options object.
-     */
-    const char *proxy_host;
-
-    /**
-     * The port to use to connect to the proxy server.
-     *
-     * Ports must be in the range (1, 65535). The value is ignored if
-     * proxy_type is TOX_PROXY_TYPE_NONE.
-     */
-    uint16_t proxy_port;
-
-    /**
-     * The start port of the inclusive port range to attempt to use.
-     *
-     * If both start_port and end_port are 0, the default port range will be
-     * used: `[33445, 33545]`.
-     *
-     * If either start_port or end_port is 0 while the other is non-zero, the
-     * non-zero port will be the only port in the range.
-     *
-     * Having start_port > end_port will yield the same behavior as if
-     * start_port and end_port were swapped.
-     */
-    uint16_t start_port;
-
-    /**
-     * The end port of the inclusive port range to attempt to use.
-     */
-    uint16_t end_port;
-
-    /**
-     * The port to use for the TCP server (relay). If 0, the TCP server is
-     * disabled.
-     *
-     * Enabling it is not required for Tox to function properly.
-     *
-     * When enabled, your Tox instance can act as a TCP relay for other Tox
-     * instance. This leads to increased traffic, thus when writing a client
-     * it is recommended to enable TCP server only if the user has an option
-     * to disable it.
-     */
-    uint16_t tcp_port;
-
-    /**
-     * Enables or disables UDP hole-punching. (Default: enabled).
-     */
-    bool hole_punching_enabled;
-
-    /**
-     * The type of savedata to load from.
-     */
-    Tox_Savedata_Type savedata_type;
-
-    /**
-     * The savedata.
-     *
-     * The data pointed at by this member is owned by the user, so must outlive
-     * the options object.
-     */
-    const uint8_t *savedata_data;
-
-    /**
-     * The length of the savedata.
-     */
-    size_t savedata_length;
-
-    /**
-     * Logging callback for the new Tox instance.
-     */
-    tox_log_cb *log_callback;
-
-    /**
-     * User data pointer passed to the logging callback.
-     */
-    void *log_user_data;
-
-    /**
-     * These options are experimental, so avoid writing code that depends on
-     * them. Options marked "experimental" may change their behaviour or go away
-     * entirely in the future, or may be renamed to something non-experimental
-     * if they become part of the supported API.
-     */
-    /**
-     * Make public API functions thread-safe using a per-instance lock.
-     *
-     * Default: false.
-     */
-    bool experimental_thread_safety;
-
-    /**
-     * Enable saving DHT-based group chats to Tox save data (via
-     * `tox_get_savedata`). This format will change in the future, so don't rely
-     * on it.
-     *
-     * As an alternative, clients can save the group chat ID in client-owned
-     * savedata. Then, when the client starts, it can use `tox_group_join`
-     * with the saved chat ID to recreate the group chat.
-     *
-     * Default: false.
-     */
-    bool experimental_groups_persistence;
-
-    /**
-     * @brief Disable DNS hostname resolution.
-     *
-     * Hostnames or IP addresses are passed to the bootstrap/add_tcp_relay
-     * function and proxy host options. If disabled (this flag is true), only
-     * IP addresses are allowed.
-     *
-     * If this is set to true, the library will not attempt to resolve
-     * hostnames. This is useful for clients that want to resolve hostnames
-     * themselves and pass the resolved IP addresses to the library (e.g. in
-     * case it wants to use Tor).
-     * Passing hostnames will result in a TOX_ERR_BOOTSTRAP_BAD_HOST error if
-     * this is set to true.
-     *
-     * Default: false. May become true in the future (0.3.0).
-     */
-    bool experimental_disable_dns;
-};
-#endif /* TOX_HIDE_DEPRECATED */
-
-bool tox_options_get_ipv6_enabled(const Tox_Options *options);
-
-void tox_options_set_ipv6_enabled(Tox_Options *options, bool ipv6_enabled);
-
-bool tox_options_get_udp_enabled(const Tox_Options *options);
-
-void tox_options_set_udp_enabled(Tox_Options *options, bool udp_enabled);
-
-bool tox_options_get_local_discovery_enabled(const Tox_Options *options);
-
-void tox_options_set_local_discovery_enabled(Tox_Options *options, bool local_discovery_enabled);
-
-bool tox_options_get_dht_announcements_enabled(const Tox_Options *options);
-
-void tox_options_set_dht_announcements_enabled(Tox_Options *options, bool dht_announcements_enabled);
-
-Tox_Proxy_Type tox_options_get_proxy_type(const Tox_Options *options);
-
-void tox_options_set_proxy_type(Tox_Options *options, Tox_Proxy_Type proxy_type);
-
-const char *tox_options_get_proxy_host(const Tox_Options *options);
-
-void tox_options_set_proxy_host(Tox_Options *options, const char *proxy_host);
-
-uint16_t tox_options_get_proxy_port(const Tox_Options *options);
-
-void tox_options_set_proxy_port(Tox_Options *options, uint16_t proxy_port);
-
-uint16_t tox_options_get_start_port(const Tox_Options *options);
-
-void tox_options_set_start_port(Tox_Options *options, uint16_t start_port);
-
-uint16_t tox_options_get_end_port(const Tox_Options *options);
-
-void tox_options_set_end_port(Tox_Options *options, uint16_t end_port);
-
-uint16_t tox_options_get_tcp_port(const Tox_Options *options);
-
-void tox_options_set_tcp_port(Tox_Options *options, uint16_t tcp_port);
-
-bool tox_options_get_hole_punching_enabled(const Tox_Options *options);
-
-void tox_options_set_hole_punching_enabled(Tox_Options *options, bool hole_punching_enabled);
-
-Tox_Savedata_Type tox_options_get_savedata_type(const Tox_Options *options);
-
-void tox_options_set_savedata_type(Tox_Options *options, Tox_Savedata_Type savedata_type);
-
-const uint8_t *tox_options_get_savedata_data(const Tox_Options *options);
-
-void tox_options_set_savedata_data(Tox_Options *options, const uint8_t savedata_data[], size_t length);
-
-size_t tox_options_get_savedata_length(const Tox_Options *options);
-
-void tox_options_set_savedata_length(Tox_Options *options, size_t savedata_length);
-
-tox_log_cb *tox_options_get_log_callback(const Tox_Options *options);
-
-void tox_options_set_log_callback(Tox_Options *options, tox_log_cb *log_callback);
-
-void *tox_options_get_log_user_data(const Tox_Options *options);
-
-void tox_options_set_log_user_data(Tox_Options *options, void *log_user_data);
-
-bool tox_options_get_experimental_thread_safety(const Tox_Options *options);
-
-void tox_options_set_experimental_thread_safety(Tox_Options *options, bool experimental_thread_safety);
-
-bool tox_options_get_experimental_groups_persistence(const Tox_Options *options);
-
-void tox_options_set_experimental_groups_persistence(Tox_Options *options, bool experimental_groups_persistence);
-
-bool tox_options_get_experimental_disable_dns(const Tox_Options *options);
-
-void tox_options_set_experimental_disable_dns(Tox_Options *options, bool experimental_disable_dns);
-
-/**
- * @brief Initialises a Tox_Options object with the default options.
- *
- * The result of this function is independent of the original options. All
- * values will be overwritten, no values will be read (so it is permissible
- * to pass an uninitialised object).
- *
- * If options is NULL, this function has no effect.
- *
- * @param options An options object to be filled with default options.
- */
-void tox_options_default(Tox_Options *options);
-
-typedef enum Tox_Err_Options_New {
-
-    /**
-     * The function returned successfully.
-     */
-    TOX_ERR_OPTIONS_NEW_OK,
-
-    /**
-     * The function failed to allocate enough memory for the options struct.
-     */
-    TOX_ERR_OPTIONS_NEW_MALLOC,
-
-} Tox_Err_Options_New;
-
-const char *tox_err_options_new_to_string(Tox_Err_Options_New value);
-
-/**
- * @brief Allocates a new Tox_Options object and initialises it with the default
- *   options.
- *
- * This function can be used to preserve long term ABI compatibility by
- * giving the responsibility of allocation and deallocation to the Tox library.
- *
- * Objects returned from this function must be freed using the tox_options_free
- * function.
- *
- * @return A new Tox_Options object with default options or NULL on failure.
- */
-Tox_Options *tox_options_new(Tox_Err_Options_New *error);
-
-/**
- * @brief Releases all resources associated with an options objects.
- *
- * Passing a pointer that was not returned by tox_options_new results in
- * undefined behaviour.
- */
-void tox_options_free(Tox_Options *options);
-
-/** @} */
-
 /** @{
  * @name Creation and destruction
  */
@@ -908,7 +479,7 @@ const char *tox_err_new_to_string(Tox_Err_New value);
  *
  * @return A new Tox instance pointer on success or NULL on failure.
  */
-Tox *tox_new(const Tox_Options *options, Tox_Err_New *error);
+Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error);
 
 /**
  * @brief Releases all resources associated with the Tox instance and
diff --git a/toxcore/tox_api.c b/toxcore/tox_api.c
index 7988018be5c..3d3c1f06522 100644
--- a/toxcore/tox_api.c
+++ b/toxcore/tox_api.c
@@ -3,18 +3,8 @@
  */
 #include "tox.h"
 
-#include <stdlib.h>
-
-#include "ccompat.h"
 #include "tox_private.h"
 
-#define SET_ERROR_PARAMETER(param, x) \
-    do {                              \
-        if (param != nullptr) {       \
-            *param = x;               \
-        }                             \
-    } while (0)
-
 uint32_t tox_version_major(void)
 {
     return TOX_VERSION_MAJOR;
@@ -136,200 +126,6 @@ uint32_t tox_dht_node_public_key_size(void)
     return TOX_DHT_NODE_PUBLIC_KEY_SIZE;
 }
 
-bool tox_options_get_ipv6_enabled(const Tox_Options *options)
-{
-    return options->ipv6_enabled;
-}
-void tox_options_set_ipv6_enabled(Tox_Options *options, bool ipv6_enabled)
-{
-    options->ipv6_enabled = ipv6_enabled;
-}
-bool tox_options_get_udp_enabled(const Tox_Options *options)
-{
-    return options->udp_enabled;
-}
-void tox_options_set_udp_enabled(Tox_Options *options, bool udp_enabled)
-{
-    options->udp_enabled = udp_enabled;
-}
-Tox_Proxy_Type tox_options_get_proxy_type(const Tox_Options *options)
-{
-    return options->proxy_type;
-}
-void tox_options_set_proxy_type(Tox_Options *options, Tox_Proxy_Type proxy_type)
-{
-    options->proxy_type = proxy_type;
-}
-const char *tox_options_get_proxy_host(const Tox_Options *options)
-{
-    return options->proxy_host;
-}
-void tox_options_set_proxy_host(Tox_Options *options, const char *proxy_host)
-{
-    options->proxy_host = proxy_host;
-}
-uint16_t tox_options_get_proxy_port(const Tox_Options *options)
-{
-    return options->proxy_port;
-}
-void tox_options_set_proxy_port(Tox_Options *options, uint16_t proxy_port)
-{
-    options->proxy_port = proxy_port;
-}
-uint16_t tox_options_get_start_port(const Tox_Options *options)
-{
-    return options->start_port;
-}
-void tox_options_set_start_port(Tox_Options *options, uint16_t start_port)
-{
-    options->start_port = start_port;
-}
-uint16_t tox_options_get_end_port(const Tox_Options *options)
-{
-    return options->end_port;
-}
-void tox_options_set_end_port(Tox_Options *options, uint16_t end_port)
-{
-    options->end_port = end_port;
-}
-uint16_t tox_options_get_tcp_port(const Tox_Options *options)
-{
-    return options->tcp_port;
-}
-void tox_options_set_tcp_port(Tox_Options *options, uint16_t tcp_port)
-{
-    options->tcp_port = tcp_port;
-}
-bool tox_options_get_hole_punching_enabled(const Tox_Options *options)
-{
-    return options->hole_punching_enabled;
-}
-void tox_options_set_hole_punching_enabled(Tox_Options *options, bool hole_punching_enabled)
-{
-    options->hole_punching_enabled = hole_punching_enabled;
-}
-Tox_Savedata_Type tox_options_get_savedata_type(const Tox_Options *options)
-{
-    return options->savedata_type;
-}
-void tox_options_set_savedata_type(Tox_Options *options, Tox_Savedata_Type savedata_type)
-{
-    options->savedata_type = savedata_type;
-}
-size_t tox_options_get_savedata_length(const Tox_Options *options)
-{
-    return options->savedata_length;
-}
-void tox_options_set_savedata_length(Tox_Options *options, size_t savedata_length)
-{
-    options->savedata_length = savedata_length;
-}
-tox_log_cb *tox_options_get_log_callback(const Tox_Options *options)
-{
-    return options->log_callback;
-}
-void tox_options_set_log_callback(Tox_Options *options, tox_log_cb *log_callback)
-{
-    options->log_callback = log_callback;
-}
-void *tox_options_get_log_user_data(const Tox_Options *options)
-{
-    return options->log_user_data;
-}
-void tox_options_set_log_user_data(Tox_Options *options, void *log_user_data)
-{
-    options->log_user_data = log_user_data;
-}
-bool tox_options_get_local_discovery_enabled(const Tox_Options *options)
-{
-    return options->local_discovery_enabled;
-}
-void tox_options_set_local_discovery_enabled(Tox_Options *options, bool local_discovery_enabled)
-{
-    options->local_discovery_enabled = local_discovery_enabled;
-}
-bool tox_options_get_dht_announcements_enabled(const Tox_Options *options)
-{
-    return options->dht_announcements_enabled;
-}
-void tox_options_set_dht_announcements_enabled(Tox_Options *options, bool dht_announcements_enabled)
-{
-    options->dht_announcements_enabled = dht_announcements_enabled;
-}
-bool tox_options_get_experimental_thread_safety(const Tox_Options *options)
-{
-    return options->experimental_thread_safety;
-}
-void tox_options_set_experimental_thread_safety(
-    Tox_Options *options, bool experimental_thread_safety)
-{
-    options->experimental_thread_safety = experimental_thread_safety;
-}
-bool tox_options_get_experimental_groups_persistence(const Tox_Options *options)
-{
-    return options->experimental_groups_persistence;
-}
-void tox_options_set_experimental_groups_persistence(
-    Tox_Options *options, bool experimental_groups_persistence)
-{
-    options->experimental_groups_persistence = experimental_groups_persistence;
-}
-bool tox_options_get_experimental_disable_dns(const Tox_Options *options)
-{
-    return options->experimental_disable_dns;
-}
-void tox_options_set_experimental_disable_dns(Tox_Options *options, bool experimental_disable_dns)
-{
-    options->experimental_disable_dns = experimental_disable_dns;
-}
-
-const uint8_t *tox_options_get_savedata_data(const Tox_Options *options)
-{
-    return options->savedata_data;
-}
-
-void tox_options_set_savedata_data(Tox_Options *options, const uint8_t *savedata_data, size_t length)
-{
-    options->savedata_data = savedata_data;
-    options->savedata_length = length;
-}
-
-void tox_options_default(Tox_Options *options)
-{
-    if (options != nullptr) {
-        const Tox_Options default_options = {false};
-        *options = default_options;
-        tox_options_set_ipv6_enabled(options, true);
-        tox_options_set_udp_enabled(options, true);
-        tox_options_set_proxy_type(options, TOX_PROXY_TYPE_NONE);
-        tox_options_set_hole_punching_enabled(options, true);
-        tox_options_set_local_discovery_enabled(options, true);
-        tox_options_set_dht_announcements_enabled(options, true);
-        tox_options_set_experimental_thread_safety(options, false);
-        tox_options_set_experimental_groups_persistence(options, false);
-        tox_options_set_experimental_disable_dns(options, false);
-    }
-}
-
-Tox_Options *tox_options_new(Tox_Err_Options_New *error)
-{
-    Tox_Options *options = (Tox_Options *)calloc(1, sizeof(Tox_Options));
-
-    if (options != nullptr) {
-        tox_options_default(options);
-        SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_OK);
-        return options;
-    }
-
-    SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_MALLOC);
-    return nullptr;
-}
-
-void tox_options_free(Tox_Options *options)
-{
-    free(options);
-}
-
 const char *tox_user_status_to_string(Tox_User_Status value)
 {
     switch (value) {
@@ -387,27 +183,6 @@ const char *tox_savedata_type_to_string(Tox_Savedata_Type value)
 
     return "<invalid Tox_Savedata_Type>";
 }
-const char *tox_log_level_to_string(Tox_Log_Level value)
-{
-    switch (value) {
-        case TOX_LOG_LEVEL_TRACE:
-            return "TOX_LOG_LEVEL_TRACE";
-
-        case TOX_LOG_LEVEL_DEBUG:
-            return "TOX_LOG_LEVEL_DEBUG";
-
-        case TOX_LOG_LEVEL_INFO:
-            return "TOX_LOG_LEVEL_INFO";
-
-        case TOX_LOG_LEVEL_WARNING:
-            return "TOX_LOG_LEVEL_WARNING";
-
-        case TOX_LOG_LEVEL_ERROR:
-            return "TOX_LOG_LEVEL_ERROR";
-    }
-
-    return "<invalid Tox_Log_Level>";
-}
 const char *tox_err_options_new_to_string(Tox_Err_Options_New value)
 {
     switch (value) {
diff --git a/toxcore/tox_attributes.h b/toxcore/tox_attributes.h
new file mode 100644
index 00000000000..e9549f029da
--- /dev/null
+++ b/toxcore/tox_attributes.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+/**
+ * nonnull attributes for GCC/Clang and Cimple.
+ *
+ * This file is a modified version of c-toxcore/toxcore/attributes.h with a
+ * `tox_` prefix added to the macros to avoid conflicts with client code.
+ */
+#ifndef C_TOXCORE_TOXCORE_TOX_ATTRIBUTES_H
+#define C_TOXCORE_TOXCORE_TOX_ATTRIBUTES_H
+
+/* No declarations here. */
+
+//!TOKSTYLE-
+
+#if defined(__GNUC__) && defined(_DEBUG) && !defined(__OPTIMIZE__)
+#define tox_non_null(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#else
+#define tox_non_null(...)
+#endif
+
+#define tox_nullable(...)
+
+#ifdef SPARSE
+#define tox_bitwise __attribute__((bitwise))
+#define tox_force __attribute__((force))
+#else
+#define tox_bitwise
+#define tox_force
+#endif
+
+//!TOKSTYLE+
+
+#endif /* C_TOXCORE_TOXCORE_TOX_ATTRIBUTES_H */
diff --git a/toxcore/tox_event.h b/toxcore/tox_event.h
index 293939c8c67..eb600569fd2 100644
--- a/toxcore/tox_event.h
+++ b/toxcore/tox_event.h
@@ -5,10 +5,10 @@
 #ifndef C_TOXCORE_TOXCORE_TOX_EVENT_H
 #define C_TOXCORE_TOXCORE_TOX_EVENT_H
 
-#include "attributes.h"
 #include "bin_pack.h"
 #include "bin_unpack.h"
 #include "mem.h"
+#include "tox_attributes.h"
 #include "tox_events.h"
 #include "tox_private.h"
 
@@ -72,186 +72,186 @@ struct Tox_Event {
 /**
  * Constructor.
  */
-non_null() bool tox_event_construct(Tox_Event *event, Tox_Event_Type type, const Memory *mem);
+tox_non_null() bool tox_event_construct(Tox_Event *event, Tox_Event_Type type, const Memory *mem);
 
-non_null() Tox_Event_Conference_Connected *tox_event_conference_connected_new(const Memory *mem);
-non_null() Tox_Event_Conference_Invite *tox_event_conference_invite_new(const Memory *mem);
-non_null() Tox_Event_Conference_Message *tox_event_conference_message_new(const Memory *mem);
-non_null() Tox_Event_Conference_Peer_List_Changed *tox_event_conference_peer_list_changed_new(const Memory *mem);
-non_null() Tox_Event_Conference_Peer_Name *tox_event_conference_peer_name_new(const Memory *mem);
-non_null() Tox_Event_Conference_Title *tox_event_conference_title_new(const Memory *mem);
-non_null() Tox_Event_File_Chunk_Request *tox_event_file_chunk_request_new(const Memory *mem);
-non_null() Tox_Event_File_Recv_Chunk *tox_event_file_recv_chunk_new(const Memory *mem);
-non_null() Tox_Event_File_Recv_Control *tox_event_file_recv_control_new(const Memory *mem);
-non_null() Tox_Event_File_Recv *tox_event_file_recv_new(const Memory *mem);
-non_null() Tox_Event_Friend_Connection_Status *tox_event_friend_connection_status_new(const Memory *mem);
-non_null() Tox_Event_Friend_Lossless_Packet *tox_event_friend_lossless_packet_new(const Memory *mem);
-non_null() Tox_Event_Friend_Lossy_Packet *tox_event_friend_lossy_packet_new(const Memory *mem);
-non_null() Tox_Event_Friend_Message *tox_event_friend_message_new(const Memory *mem);
-non_null() Tox_Event_Friend_Name *tox_event_friend_name_new(const Memory *mem);
-non_null() Tox_Event_Friend_Read_Receipt *tox_event_friend_read_receipt_new(const Memory *mem);
-non_null() Tox_Event_Friend_Request *tox_event_friend_request_new(const Memory *mem);
-non_null() Tox_Event_Friend_Status_Message *tox_event_friend_status_message_new(const Memory *mem);
-non_null() Tox_Event_Friend_Status *tox_event_friend_status_new(const Memory *mem);
-non_null() Tox_Event_Friend_Typing *tox_event_friend_typing_new(const Memory *mem);
-non_null() Tox_Event_Self_Connection_Status *tox_event_self_connection_status_new(const Memory *mem);
-non_null() Tox_Event_Group_Peer_Name *tox_event_group_peer_name_new(const Memory *mem);
-non_null() Tox_Event_Group_Peer_Status *tox_event_group_peer_status_new(const Memory *mem);
-non_null() Tox_Event_Group_Topic *tox_event_group_topic_new(const Memory *mem);
-non_null() Tox_Event_Group_Privacy_State *tox_event_group_privacy_state_new(const Memory *mem);
-non_null() Tox_Event_Group_Voice_State *tox_event_group_voice_state_new(const Memory *mem);
-non_null() Tox_Event_Group_Topic_Lock *tox_event_group_topic_lock_new(const Memory *mem);
-non_null() Tox_Event_Group_Peer_Limit *tox_event_group_peer_limit_new(const Memory *mem);
-non_null() Tox_Event_Group_Password *tox_event_group_password_new(const Memory *mem);
-non_null() Tox_Event_Group_Message *tox_event_group_message_new(const Memory *mem);
-non_null() Tox_Event_Group_Private_Message *tox_event_group_private_message_new(const Memory *mem);
-non_null() Tox_Event_Group_Custom_Packet *tox_event_group_custom_packet_new(const Memory *mem);
-non_null() Tox_Event_Group_Custom_Private_Packet *tox_event_group_custom_private_packet_new(const Memory *mem);
-non_null() Tox_Event_Group_Invite *tox_event_group_invite_new(const Memory *mem);
-non_null() Tox_Event_Group_Peer_Join *tox_event_group_peer_join_new(const Memory *mem);
-non_null() Tox_Event_Group_Peer_Exit *tox_event_group_peer_exit_new(const Memory *mem);
-non_null() Tox_Event_Group_Self_Join *tox_event_group_self_join_new(const Memory *mem);
-non_null() Tox_Event_Group_Join_Fail *tox_event_group_join_fail_new(const Memory *mem);
-non_null() Tox_Event_Group_Moderation *tox_event_group_moderation_new(const Memory *mem);
-non_null() Tox_Event_Dht_Get_Nodes_Response *tox_event_dht_get_nodes_response_new(const Memory *mem);
+tox_non_null() Tox_Event_Conference_Connected *tox_event_conference_connected_new(const Memory *mem);
+tox_non_null() Tox_Event_Conference_Invite *tox_event_conference_invite_new(const Memory *mem);
+tox_non_null() Tox_Event_Conference_Message *tox_event_conference_message_new(const Memory *mem);
+tox_non_null() Tox_Event_Conference_Peer_List_Changed *tox_event_conference_peer_list_changed_new(const Memory *mem);
+tox_non_null() Tox_Event_Conference_Peer_Name *tox_event_conference_peer_name_new(const Memory *mem);
+tox_non_null() Tox_Event_Conference_Title *tox_event_conference_title_new(const Memory *mem);
+tox_non_null() Tox_Event_File_Chunk_Request *tox_event_file_chunk_request_new(const Memory *mem);
+tox_non_null() Tox_Event_File_Recv_Chunk *tox_event_file_recv_chunk_new(const Memory *mem);
+tox_non_null() Tox_Event_File_Recv_Control *tox_event_file_recv_control_new(const Memory *mem);
+tox_non_null() Tox_Event_File_Recv *tox_event_file_recv_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Connection_Status *tox_event_friend_connection_status_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Lossless_Packet *tox_event_friend_lossless_packet_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Lossy_Packet *tox_event_friend_lossy_packet_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Message *tox_event_friend_message_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Name *tox_event_friend_name_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Read_Receipt *tox_event_friend_read_receipt_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Request *tox_event_friend_request_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Status_Message *tox_event_friend_status_message_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Status *tox_event_friend_status_new(const Memory *mem);
+tox_non_null() Tox_Event_Friend_Typing *tox_event_friend_typing_new(const Memory *mem);
+tox_non_null() Tox_Event_Self_Connection_Status *tox_event_self_connection_status_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Peer_Name *tox_event_group_peer_name_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Peer_Status *tox_event_group_peer_status_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Topic *tox_event_group_topic_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Privacy_State *tox_event_group_privacy_state_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Voice_State *tox_event_group_voice_state_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Topic_Lock *tox_event_group_topic_lock_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Peer_Limit *tox_event_group_peer_limit_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Password *tox_event_group_password_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Message *tox_event_group_message_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Private_Message *tox_event_group_private_message_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Custom_Packet *tox_event_group_custom_packet_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Custom_Private_Packet *tox_event_group_custom_private_packet_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Invite *tox_event_group_invite_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Peer_Join *tox_event_group_peer_join_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Peer_Exit *tox_event_group_peer_exit_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Self_Join *tox_event_group_self_join_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Join_Fail *tox_event_group_join_fail_new(const Memory *mem);
+tox_non_null() Tox_Event_Group_Moderation *tox_event_group_moderation_new(const Memory *mem);
+tox_non_null() Tox_Event_Dht_Get_Nodes_Response *tox_event_dht_get_nodes_response_new(const Memory *mem);
 
 /**
  * Destructor.
  */
-non_null(2) nullable(1) void tox_event_destruct(Tox_Event *event, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_destruct(Tox_Event *event, const Memory *mem);
 
-non_null(2) nullable(1) void tox_event_conference_connected_free(Tox_Event_Conference_Connected *conference_connected, const Memory *mem);
-non_null(2) nullable(1) void tox_event_conference_invite_free(Tox_Event_Conference_Invite *conference_invite, const Memory *mem);
-non_null(2) nullable(1) void tox_event_conference_message_free(Tox_Event_Conference_Message *conference_message, const Memory *mem);
-non_null(2) nullable(1) void tox_event_conference_peer_list_changed_free(Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed, const Memory *mem);
-non_null(2) nullable(1) void tox_event_conference_peer_name_free(Tox_Event_Conference_Peer_Name *conference_peer_name, const Memory *mem);
-non_null(2) nullable(1) void tox_event_conference_title_free(Tox_Event_Conference_Title *conference_title, const Memory *mem);
-non_null(2) nullable(1) void tox_event_file_chunk_request_free(Tox_Event_File_Chunk_Request *file_chunk_request, const Memory *mem);
-non_null(2) nullable(1) void tox_event_file_recv_chunk_free(Tox_Event_File_Recv_Chunk *file_recv_chunk, const Memory *mem);
-non_null(2) nullable(1) void tox_event_file_recv_control_free(Tox_Event_File_Recv_Control *file_recv_control, const Memory *mem);
-non_null(2) nullable(1) void tox_event_file_recv_free(Tox_Event_File_Recv *file_recv, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_connection_status_free(Tox_Event_Friend_Connection_Status *friend_connection_status, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_lossless_packet_free(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_lossy_packet_free(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_message_free(Tox_Event_Friend_Message *friend_message, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_name_free(Tox_Event_Friend_Name *friend_name, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_read_receipt_free(Tox_Event_Friend_Read_Receipt *friend_read_receipt, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_request_free(Tox_Event_Friend_Request *friend_request, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_status_message_free(Tox_Event_Friend_Status_Message *friend_status_message, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_status_free(Tox_Event_Friend_Status *friend_status, const Memory *mem);
-non_null(2) nullable(1) void tox_event_friend_typing_free(Tox_Event_Friend_Typing *friend_typing, const Memory *mem);
-non_null(2) nullable(1) void tox_event_self_connection_status_free(Tox_Event_Self_Connection_Status *self_connection_status, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_peer_name_free(Tox_Event_Group_Peer_Name *group_peer_name, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_peer_status_free(Tox_Event_Group_Peer_Status *group_peer_status, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_topic_free(Tox_Event_Group_Topic *group_topic, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_privacy_state_free(Tox_Event_Group_Privacy_State *group_privacy_state, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_voice_state_free(Tox_Event_Group_Voice_State *group_voice_state, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_topic_lock_free(Tox_Event_Group_Topic_Lock *group_topic_lock, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_peer_limit_free(Tox_Event_Group_Peer_Limit *group_peer_limit, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_password_free(Tox_Event_Group_Password *group_password, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_message_free(Tox_Event_Group_Message *group_message, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_private_message_free(Tox_Event_Group_Private_Message *group_private_message, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_custom_packet_free(Tox_Event_Group_Custom_Packet *group_custom_packet, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_custom_private_packet_free(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_invite_free(Tox_Event_Group_Invite *group_invite, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_peer_join_free(Tox_Event_Group_Peer_Join *group_peer_join, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_peer_exit_free(Tox_Event_Group_Peer_Exit *group_peer_exit, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_self_join_free(Tox_Event_Group_Self_Join *group_self_join, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_join_fail_free(Tox_Event_Group_Join_Fail *group_join_fail, const Memory *mem);
-non_null(2) nullable(1) void tox_event_group_moderation_free(Tox_Event_Group_Moderation *group_moderation, const Memory *mem);
-non_null(2) nullable(1) void tox_event_dht_get_nodes_response_free(Tox_Event_Dht_Get_Nodes_Response *dht_get_nodes_response, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_conference_connected_free(Tox_Event_Conference_Connected *conference_connected, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_conference_invite_free(Tox_Event_Conference_Invite *conference_invite, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_conference_message_free(Tox_Event_Conference_Message *conference_message, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_conference_peer_list_changed_free(Tox_Event_Conference_Peer_List_Changed *conference_peer_list_changed, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_conference_peer_name_free(Tox_Event_Conference_Peer_Name *conference_peer_name, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_conference_title_free(Tox_Event_Conference_Title *conference_title, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_file_chunk_request_free(Tox_Event_File_Chunk_Request *file_chunk_request, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_file_recv_chunk_free(Tox_Event_File_Recv_Chunk *file_recv_chunk, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_file_recv_control_free(Tox_Event_File_Recv_Control *file_recv_control, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_file_recv_free(Tox_Event_File_Recv *file_recv, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_connection_status_free(Tox_Event_Friend_Connection_Status *friend_connection_status, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_lossless_packet_free(Tox_Event_Friend_Lossless_Packet *friend_lossless_packet, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_lossy_packet_free(Tox_Event_Friend_Lossy_Packet *friend_lossy_packet, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_message_free(Tox_Event_Friend_Message *friend_message, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_name_free(Tox_Event_Friend_Name *friend_name, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_read_receipt_free(Tox_Event_Friend_Read_Receipt *friend_read_receipt, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_request_free(Tox_Event_Friend_Request *friend_request, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_status_message_free(Tox_Event_Friend_Status_Message *friend_status_message, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_status_free(Tox_Event_Friend_Status *friend_status, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_friend_typing_free(Tox_Event_Friend_Typing *friend_typing, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_self_connection_status_free(Tox_Event_Self_Connection_Status *self_connection_status, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_peer_name_free(Tox_Event_Group_Peer_Name *group_peer_name, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_peer_status_free(Tox_Event_Group_Peer_Status *group_peer_status, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_topic_free(Tox_Event_Group_Topic *group_topic, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_privacy_state_free(Tox_Event_Group_Privacy_State *group_privacy_state, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_voice_state_free(Tox_Event_Group_Voice_State *group_voice_state, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_topic_lock_free(Tox_Event_Group_Topic_Lock *group_topic_lock, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_peer_limit_free(Tox_Event_Group_Peer_Limit *group_peer_limit, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_password_free(Tox_Event_Group_Password *group_password, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_message_free(Tox_Event_Group_Message *group_message, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_private_message_free(Tox_Event_Group_Private_Message *group_private_message, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_custom_packet_free(Tox_Event_Group_Custom_Packet *group_custom_packet, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_custom_private_packet_free(Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_invite_free(Tox_Event_Group_Invite *group_invite, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_peer_join_free(Tox_Event_Group_Peer_Join *group_peer_join, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_peer_exit_free(Tox_Event_Group_Peer_Exit *group_peer_exit, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_self_join_free(Tox_Event_Group_Self_Join *group_self_join, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_join_fail_free(Tox_Event_Group_Join_Fail *group_join_fail, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_group_moderation_free(Tox_Event_Group_Moderation *group_moderation, const Memory *mem);
+tox_non_null(2) tox_nullable(1) void tox_event_dht_get_nodes_response_free(Tox_Event_Dht_Get_Nodes_Response *dht_get_nodes_response, const Memory *mem);
 
 /**
  * Pack into msgpack.
  */
-non_null() bool tox_event_pack(const Tox_Event *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_pack(const Tox_Event *event, Bin_Pack *bp);
 
-non_null() bool tox_event_conference_connected_pack(const Tox_Event_Conference_Connected *event, Bin_Pack *bp);
-non_null() bool tox_event_conference_invite_pack(const Tox_Event_Conference_Invite *event, Bin_Pack *bp);
-non_null() bool tox_event_conference_message_pack(const Tox_Event_Conference_Message *event, Bin_Pack *bp);
-non_null() bool tox_event_conference_peer_list_changed_pack(const Tox_Event_Conference_Peer_List_Changed *event, Bin_Pack *bp);
-non_null() bool tox_event_conference_peer_name_pack(const Tox_Event_Conference_Peer_Name *event, Bin_Pack *bp);
-non_null() bool tox_event_conference_title_pack(const Tox_Event_Conference_Title *event, Bin_Pack *bp);
-non_null() bool tox_event_file_chunk_request_pack(const Tox_Event_File_Chunk_Request *event, Bin_Pack *bp);
-non_null() bool tox_event_file_recv_chunk_pack(const Tox_Event_File_Recv_Chunk *event, Bin_Pack *bp);
-non_null() bool tox_event_file_recv_control_pack(const Tox_Event_File_Recv_Control *event, Bin_Pack *bp);
-non_null() bool tox_event_file_recv_pack(const Tox_Event_File_Recv *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_connection_status_pack(const Tox_Event_Friend_Connection_Status *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_lossless_packet_pack(const Tox_Event_Friend_Lossless_Packet *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_lossy_packet_pack(const Tox_Event_Friend_Lossy_Packet *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_message_pack(const Tox_Event_Friend_Message *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_name_pack(const Tox_Event_Friend_Name *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_read_receipt_pack(const Tox_Event_Friend_Read_Receipt *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_request_pack(const Tox_Event_Friend_Request *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_status_message_pack(const Tox_Event_Friend_Status_Message *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_status_pack(const Tox_Event_Friend_Status *event, Bin_Pack *bp);
-non_null() bool tox_event_friend_typing_pack(const Tox_Event_Friend_Typing *event, Bin_Pack *bp);
-non_null() bool tox_event_self_connection_status_pack(const Tox_Event_Self_Connection_Status *event, Bin_Pack *bp);
-non_null() bool tox_event_group_peer_name_pack(const Tox_Event_Group_Peer_Name *event, Bin_Pack *bp);
-non_null() bool tox_event_group_peer_status_pack(const Tox_Event_Group_Peer_Status *event, Bin_Pack *bp);
-non_null() bool tox_event_group_topic_pack(const Tox_Event_Group_Topic *event, Bin_Pack *bp);
-non_null() bool tox_event_group_privacy_state_pack(const Tox_Event_Group_Privacy_State *event, Bin_Pack *bp);
-non_null() bool tox_event_group_voice_state_pack(const Tox_Event_Group_Voice_State *event, Bin_Pack *bp);
-non_null() bool tox_event_group_topic_lock_pack(const Tox_Event_Group_Topic_Lock *event, Bin_Pack *bp);
-non_null() bool tox_event_group_peer_limit_pack(const Tox_Event_Group_Peer_Limit *event, Bin_Pack *bp);
-non_null() bool tox_event_group_password_pack(const Tox_Event_Group_Password *event, Bin_Pack *bp);
-non_null() bool tox_event_group_message_pack(const Tox_Event_Group_Message *event, Bin_Pack *bp);
-non_null() bool tox_event_group_private_message_pack(const Tox_Event_Group_Private_Message *event, Bin_Pack *bp);
-non_null() bool tox_event_group_custom_packet_pack(const Tox_Event_Group_Custom_Packet *event, Bin_Pack *bp);
-non_null() bool tox_event_group_custom_private_packet_pack(const Tox_Event_Group_Custom_Private_Packet *event, Bin_Pack *bp);
-non_null() bool tox_event_group_invite_pack(const Tox_Event_Group_Invite *event, Bin_Pack *bp);
-non_null() bool tox_event_group_peer_join_pack(const Tox_Event_Group_Peer_Join *event, Bin_Pack *bp);
-non_null() bool tox_event_group_peer_exit_pack(const Tox_Event_Group_Peer_Exit *event, Bin_Pack *bp);
-non_null() bool tox_event_group_self_join_pack(const Tox_Event_Group_Self_Join *event, Bin_Pack *bp);
-non_null() bool tox_event_group_join_fail_pack(const Tox_Event_Group_Join_Fail *event, Bin_Pack *bp);
-non_null() bool tox_event_group_moderation_pack(const Tox_Event_Group_Moderation *event, Bin_Pack *bp);
-non_null() bool tox_event_dht_get_nodes_response_pack(const Tox_Event_Dht_Get_Nodes_Response *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_conference_connected_pack(const Tox_Event_Conference_Connected *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_conference_invite_pack(const Tox_Event_Conference_Invite *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_conference_message_pack(const Tox_Event_Conference_Message *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_conference_peer_list_changed_pack(const Tox_Event_Conference_Peer_List_Changed *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_conference_peer_name_pack(const Tox_Event_Conference_Peer_Name *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_conference_title_pack(const Tox_Event_Conference_Title *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_file_chunk_request_pack(const Tox_Event_File_Chunk_Request *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_file_recv_chunk_pack(const Tox_Event_File_Recv_Chunk *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_file_recv_control_pack(const Tox_Event_File_Recv_Control *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_file_recv_pack(const Tox_Event_File_Recv *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_connection_status_pack(const Tox_Event_Friend_Connection_Status *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_lossless_packet_pack(const Tox_Event_Friend_Lossless_Packet *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_lossy_packet_pack(const Tox_Event_Friend_Lossy_Packet *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_message_pack(const Tox_Event_Friend_Message *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_name_pack(const Tox_Event_Friend_Name *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_read_receipt_pack(const Tox_Event_Friend_Read_Receipt *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_request_pack(const Tox_Event_Friend_Request *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_status_message_pack(const Tox_Event_Friend_Status_Message *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_status_pack(const Tox_Event_Friend_Status *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_friend_typing_pack(const Tox_Event_Friend_Typing *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_self_connection_status_pack(const Tox_Event_Self_Connection_Status *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_peer_name_pack(const Tox_Event_Group_Peer_Name *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_peer_status_pack(const Tox_Event_Group_Peer_Status *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_topic_pack(const Tox_Event_Group_Topic *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_privacy_state_pack(const Tox_Event_Group_Privacy_State *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_voice_state_pack(const Tox_Event_Group_Voice_State *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_topic_lock_pack(const Tox_Event_Group_Topic_Lock *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_peer_limit_pack(const Tox_Event_Group_Peer_Limit *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_password_pack(const Tox_Event_Group_Password *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_message_pack(const Tox_Event_Group_Message *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_private_message_pack(const Tox_Event_Group_Private_Message *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_custom_packet_pack(const Tox_Event_Group_Custom_Packet *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_custom_private_packet_pack(const Tox_Event_Group_Custom_Private_Packet *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_invite_pack(const Tox_Event_Group_Invite *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_peer_join_pack(const Tox_Event_Group_Peer_Join *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_peer_exit_pack(const Tox_Event_Group_Peer_Exit *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_self_join_pack(const Tox_Event_Group_Self_Join *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_join_fail_pack(const Tox_Event_Group_Join_Fail *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_group_moderation_pack(const Tox_Event_Group_Moderation *event, Bin_Pack *bp);
+tox_non_null() bool tox_event_dht_get_nodes_response_pack(const Tox_Event_Dht_Get_Nodes_Response *event, Bin_Pack *bp);
 
 /**
  * Unpack from msgpack.
  */
-non_null() bool tox_event_unpack_into(Tox_Event *event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_unpack_into(Tox_Event *event, Bin_Unpack *bu, const Memory *mem);
 
-non_null() bool tox_event_conference_connected_unpack(Tox_Event_Conference_Connected **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_conference_invite_unpack(Tox_Event_Conference_Invite **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_conference_message_unpack(Tox_Event_Conference_Message **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_conference_peer_list_changed_unpack(Tox_Event_Conference_Peer_List_Changed **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_conference_peer_name_unpack(Tox_Event_Conference_Peer_Name **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_conference_title_unpack(Tox_Event_Conference_Title **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_file_chunk_request_unpack(Tox_Event_File_Chunk_Request **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_file_recv_chunk_unpack(Tox_Event_File_Recv_Chunk **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_file_recv_control_unpack(Tox_Event_File_Recv_Control **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_file_recv_unpack(Tox_Event_File_Recv **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_connection_status_unpack(Tox_Event_Friend_Connection_Status **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_lossless_packet_unpack(Tox_Event_Friend_Lossless_Packet **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_lossy_packet_unpack(Tox_Event_Friend_Lossy_Packet **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_message_unpack(Tox_Event_Friend_Message **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_name_unpack(Tox_Event_Friend_Name **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_read_receipt_unpack(Tox_Event_Friend_Read_Receipt **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_request_unpack(Tox_Event_Friend_Request **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_status_message_unpack(Tox_Event_Friend_Status_Message **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_status_unpack(Tox_Event_Friend_Status **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_friend_typing_unpack(Tox_Event_Friend_Typing **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_self_connection_status_unpack(Tox_Event_Self_Connection_Status **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_peer_name_unpack(Tox_Event_Group_Peer_Name **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_peer_status_unpack(Tox_Event_Group_Peer_Status **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_topic_unpack(Tox_Event_Group_Topic **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_privacy_state_unpack(Tox_Event_Group_Privacy_State **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_voice_state_unpack(Tox_Event_Group_Voice_State **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_topic_lock_unpack(Tox_Event_Group_Topic_Lock **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_peer_limit_unpack(Tox_Event_Group_Peer_Limit **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_password_unpack(Tox_Event_Group_Password **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_message_unpack(Tox_Event_Group_Message **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_private_message_unpack(Tox_Event_Group_Private_Message **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_custom_packet_unpack(Tox_Event_Group_Custom_Packet **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_custom_private_packet_unpack(Tox_Event_Group_Custom_Private_Packet **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_invite_unpack(Tox_Event_Group_Invite **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_peer_join_unpack(Tox_Event_Group_Peer_Join **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_peer_exit_unpack(Tox_Event_Group_Peer_Exit **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_self_join_unpack(Tox_Event_Group_Self_Join **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_join_fail_unpack(Tox_Event_Group_Join_Fail **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_group_moderation_unpack(Tox_Event_Group_Moderation **event, Bin_Unpack *bu, const Memory *mem);
-non_null() bool tox_event_dht_get_nodes_response_unpack(Tox_Event_Dht_Get_Nodes_Response **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_conference_connected_unpack(Tox_Event_Conference_Connected **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_conference_invite_unpack(Tox_Event_Conference_Invite **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_conference_message_unpack(Tox_Event_Conference_Message **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_conference_peer_list_changed_unpack(Tox_Event_Conference_Peer_List_Changed **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_conference_peer_name_unpack(Tox_Event_Conference_Peer_Name **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_conference_title_unpack(Tox_Event_Conference_Title **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_file_chunk_request_unpack(Tox_Event_File_Chunk_Request **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_file_recv_chunk_unpack(Tox_Event_File_Recv_Chunk **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_file_recv_control_unpack(Tox_Event_File_Recv_Control **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_file_recv_unpack(Tox_Event_File_Recv **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_connection_status_unpack(Tox_Event_Friend_Connection_Status **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_lossless_packet_unpack(Tox_Event_Friend_Lossless_Packet **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_lossy_packet_unpack(Tox_Event_Friend_Lossy_Packet **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_message_unpack(Tox_Event_Friend_Message **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_name_unpack(Tox_Event_Friend_Name **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_read_receipt_unpack(Tox_Event_Friend_Read_Receipt **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_request_unpack(Tox_Event_Friend_Request **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_status_message_unpack(Tox_Event_Friend_Status_Message **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_status_unpack(Tox_Event_Friend_Status **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_friend_typing_unpack(Tox_Event_Friend_Typing **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_self_connection_status_unpack(Tox_Event_Self_Connection_Status **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_peer_name_unpack(Tox_Event_Group_Peer_Name **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_peer_status_unpack(Tox_Event_Group_Peer_Status **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_topic_unpack(Tox_Event_Group_Topic **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_privacy_state_unpack(Tox_Event_Group_Privacy_State **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_voice_state_unpack(Tox_Event_Group_Voice_State **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_topic_lock_unpack(Tox_Event_Group_Topic_Lock **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_peer_limit_unpack(Tox_Event_Group_Peer_Limit **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_password_unpack(Tox_Event_Group_Password **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_message_unpack(Tox_Event_Group_Message **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_private_message_unpack(Tox_Event_Group_Private_Message **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_custom_packet_unpack(Tox_Event_Group_Custom_Packet **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_custom_private_packet_unpack(Tox_Event_Group_Custom_Private_Packet **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_invite_unpack(Tox_Event_Group_Invite **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_peer_join_unpack(Tox_Event_Group_Peer_Join **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_peer_exit_unpack(Tox_Event_Group_Peer_Exit **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_self_join_unpack(Tox_Event_Group_Self_Join **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_join_fail_unpack(Tox_Event_Group_Join_Fail **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_group_moderation_unpack(Tox_Event_Group_Moderation **event, Bin_Unpack *bu, const Memory *mem);
+tox_non_null() bool tox_event_dht_get_nodes_response_unpack(Tox_Event_Dht_Get_Nodes_Response **event, Bin_Unpack *bu, const Memory *mem);
 
 #ifdef __cplusplus
 } /* extern "C" */
diff --git a/toxcore/tox_events.c b/toxcore/tox_events.c
index 0f2b1f2b8fe..500e9eac3c3 100644
--- a/toxcore/tox_events.c
+++ b/toxcore/tox_events.c
@@ -16,8 +16,10 @@
 #include "mem.h"
 #include "tox.h"
 #include "tox_event.h"
+#include "tox_impl.h"
 #include "tox_private.h"
-#include "tox_struct.h"
+#include "tox_system.h"
+#include "tox_system_impl.h"
 
 /*****************************************************
  *
@@ -173,7 +175,7 @@ Tox_Events *tox_events_load(const Tox_System *sys, const uint8_t *bytes, uint32_
     };
     events->mem = sys->mem;
 
-    if (!bin_unpack_obj(tox_events_unpack_handler, events, bytes, bytes_size)) {
+    if (!bin_unpack_obj(tox_events_unpack_handler, events, bytes, bytes_size, sys->mem)) {
         tox_events_free(events);
         return nullptr;
     }
diff --git a/toxcore/tox_events.h b/toxcore/tox_events.h
index 545279026d9..e84cea4d8f7 100644
--- a/toxcore/tox_events.h
+++ b/toxcore/tox_events.h
@@ -577,8 +577,6 @@ void tox_events_free(Tox_Events *events);
 uint32_t tox_events_bytes_size(const Tox_Events *events);
 bool tox_events_get_bytes(const Tox_Events *events, uint8_t *bytes);
 
-typedef struct Tox_System Tox_System;
-
 Tox_Events *tox_events_load(const Tox_System *sys, const uint8_t *bytes, uint32_t bytes_size);
 
 bool tox_events_equal(const Tox_System *sys, const Tox_Events *a, const Tox_Events *b);
diff --git a/toxcore/tox_events_test.cc b/toxcore/tox_events_test.cc
index 749d6768f3f..9352d324ad0 100644
--- a/toxcore/tox_events_test.cc
+++ b/toxcore/tox_events_test.cc
@@ -6,32 +6,34 @@
 #include <vector>
 
 #include "crypto_core.h"
+#include "os_system.h"
 #include "tox_private.h"
+#include "tox_system_impl.h"
 
 namespace {
 
 TEST(ToxEvents, UnpackRandomDataDoesntCrash)
 {
-    const Tox_System sys = tox_default_system();
-    ASSERT_NE(sys.rng, nullptr);
+    const Tox_System *sys = os_system();
+    ASSERT_NE(sys->rng, nullptr);
     std::array<uint8_t, 128> data;
-    random_bytes(sys.rng, data.data(), data.size());
-    tox_events_free(tox_events_load(&sys, data.data(), data.size()));
+    random_bytes(sys->rng, data.data(), data.size());
+    tox_events_free(tox_events_load(sys, data.data(), data.size()));
 }
 
 TEST(ToxEvents, UnpackEmptyDataFails)
 {
-    const Tox_System sys = tox_default_system();
+    const Tox_System *sys = os_system();
     std::array<uint8_t, 1> data;
-    Tox_Events *events = tox_events_load(&sys, data.end(), 0);
+    Tox_Events *events = tox_events_load(sys, data.end(), 0);
     EXPECT_EQ(events, nullptr);
 }
 
 TEST(ToxEvents, UnpackEmptyArrayCreatesEmptyEvents)
 {
-    const Tox_System sys = tox_default_system();
+    const Tox_System *sys = os_system();
     std::array<uint8_t, 1> data{0x90};  // empty msgpack array
-    Tox_Events *events = tox_events_load(&sys, data.data(), data.size());
+    Tox_Events *events = tox_events_load(sys, data.data(), data.size());
     ASSERT_NE(events, nullptr);
     EXPECT_EQ(tox_events_get_size(events), 0);
     tox_events_free(events);
@@ -47,10 +49,10 @@ TEST(ToxEvents, NullEventsPacksToEmptyArray)
 
 TEST(ToxEvents, PackedEventsCanBeUnpacked)
 {
-    const Tox_System sys = tox_default_system();
+    const Tox_System *sys = os_system();
     // [[0, 1]] == Tox_Self_Connection_Status { .connection_status = TOX_CONNECTION_TCP }
     std::array<uint8_t, 6> packed{0x91, 0x92, 0xcc, 0x00, 0xcc, 0x01};
-    Tox_Events *events = tox_events_load(&sys, packed.data(), packed.size());
+    Tox_Events *events = tox_events_load(sys, packed.data(), packed.size());
     ASSERT_NE(events, nullptr);
     std::array<uint8_t, 4> bytes;
     ASSERT_EQ(tox_events_bytes_size(events), bytes.size());
@@ -61,9 +63,9 @@ TEST(ToxEvents, PackedEventsCanBeUnpacked)
 
 TEST(ToxEvents, DealsWithHugeMsgpackArrays)
 {
-    const Tox_System sys = tox_default_system();
+    const Tox_System *sys = os_system();
     std::vector<uint8_t> data{0xdd, 0xff, 0xff, 0xff, 0xff};
-    EXPECT_EQ(tox_events_load(&sys, data.data(), data.size()), nullptr);
+    EXPECT_EQ(tox_events_load(sys, data.data(), data.size()), nullptr);
 }
 
 }  // namespace
diff --git a/toxcore/tox_struct.h b/toxcore/tox_impl.h
similarity index 93%
rename from toxcore/tox_struct.h
rename to toxcore/tox_impl.h
index 88f74ead628..b88d831eae6 100644
--- a/toxcore/tox_struct.h
+++ b/toxcore/tox_impl.h
@@ -3,26 +3,27 @@
  * Copyright © 2013 Tox project.
  */
 
-#ifndef C_TOXCORE_TOXCORE_TOX_STRUCT_H
-#define C_TOXCORE_TOXCORE_TOX_STRUCT_H
-
-#include <pthread.h>
+#ifndef C_TOXCORE_TOXCORE_TOX_IMPL_H
+#define C_TOXCORE_TOXCORE_TOX_IMPL_H
 
 #include "Messenger.h"
 #include "mem.h"
 #include "mono_time.h"
 #include "tox.h"
 #include "tox_private.h"
+#include "tox_system_impl.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+typedef struct Tox_Mutex Tox_Mutex;
+
 struct Tox {
     Messenger *m;
     Mono_Time *mono_time;
     Tox_System sys;
-    pthread_mutex_t *mutex;
+    Tox_Mutex *mutex;
 
     tox_log_cb *log_callback;
     tox_self_connection_status_cb *self_connection_status_callback;
@@ -73,4 +74,4 @@ struct Tox {
 } /* extern "C" */
 #endif
 
-#endif /* C_TOXCORE_TOXCORE_TOX_STRUCT_H */
+#endif /* C_TOXCORE_TOXCORE_TOX_IMPL_H */
diff --git a/toxcore/tox_log.c b/toxcore/tox_log.c
new file mode 100644
index 00000000000..82e50f217bf
--- /dev/null
+++ b/toxcore/tox_log.c
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "tox_log.h"
+
+#include "ccompat.h"
+#include "tox_log_impl.h"
+#include "tox_memory.h"
+
+Tox_Log *tox_log_new(const Tox_Log_Funcs *funcs, void *user_data, const Tox_Memory *mem)
+{
+    Tox_Log *log = (Tox_Log *)tox_memory_alloc(mem, sizeof(Tox_Log));
+
+    if (log == nullptr) {
+        return nullptr;
+    }
+
+    log->funcs = funcs;
+    log->user_data = user_data;
+
+    log->mem = mem;
+
+    return log;
+}
+
+void tox_log_free(Tox_Log *log)
+{
+    if (log == nullptr || log->mem == nullptr) {
+        return;
+    }
+    tox_memory_dealloc(log->mem, log);
+}
+
+void tox_log_log(
+    const Tox_Log *log, Tox_Log_Level level,
+    const char *file, uint32_t line, const char *func,
+    const char *message)
+{
+    log->funcs->log_callback(log->user_data, level, file, line, func, message);
+}
diff --git a/toxcore/tox_log.h b/toxcore/tox_log.h
new file mode 100644
index 00000000000..fd4485ce16e
--- /dev/null
+++ b/toxcore/tox_log.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_LOG_H
+#define C_TOXCORE_TOXCORE_TOX_LOG_H
+
+#include <stdint.h>     // uint32_t
+
+#include "tox_attributes.h"
+#include "tox_log_level.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TOX_MEMORY_DEFINED
+#define TOX_MEMORY_DEFINED
+typedef struct Tox_Memory Tox_Memory;
+#endif /* TOX_MEMORY_DEFINED */
+
+typedef struct Tox_Log_Funcs Tox_Log_Funcs;
+
+typedef struct Tox_Log Tox_Log;
+
+tox_non_null(1, 3) tox_nullable(2)
+Tox_Log *tox_log_new(const Tox_Log_Funcs *funcs, void *user_data, const Tox_Memory *mem);
+
+tox_nullable(1)
+void tox_log_free(Tox_Log *log);
+
+tox_non_null()
+void tox_log_log(
+    const Tox_Log *log, Tox_Log_Level level,
+    const char *file, uint32_t line, const char *func,
+    const char *message);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_LOG_H */
diff --git a/toxcore/tox_log_impl.h b/toxcore/tox_log_impl.h
new file mode 100644
index 00000000000..fbdcec74979
--- /dev/null
+++ b/toxcore/tox_log_impl.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_LOG_IMPL_H
+#define C_TOXCORE_TOXCORE_TOX_LOG_IMPL_H
+
+#include "tox_log.h"
+#include "tox_memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief This event is triggered when the toxcore library logs a message.
+ *
+ * This is mostly useful for debugging. This callback can be called from any
+ * function, not just tox_iterate. This means the user data lifetime must at
+ * least extend between registering and unregistering it or tox_kill.
+ *
+ * Other toxcore modules such as toxav may concurrently call this callback at
+ * any time. Thus, user code must make sure it is equipped to handle concurrent
+ * execution, e.g. by employing appropriate mutex locking.
+ *
+ * @param self The user data pointer passed to `tox_log_new`.
+ * @param level The severity of the log message.
+ * @param file The source file from which the message originated.
+ * @param line The source line from which the message originated.
+ * @param func The function from which the message originated.
+ * @param message The log message.
+ */
+typedef void tox_log_log_cb(
+    void *self, Tox_Log_Level level,
+    const char *file, uint32_t line, const char *func,
+    const char *message);
+
+
+struct Tox_Log_Funcs {
+    tox_log_log_cb *log_callback;
+};
+
+struct Tox_Log {
+    const Tox_Log_Funcs *funcs;
+    void *user_data;
+
+    const Tox_Memory *mem;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_LOG_IMPL_H */
diff --git a/toxcore/tox_log_level.c b/toxcore/tox_log_level.c
new file mode 100644
index 00000000000..d14275a481c
--- /dev/null
+++ b/toxcore/tox_log_level.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2024 The TokTok team.
+ */
+#include "tox_log_level.h"
+
+const char *tox_log_level_to_string(Tox_Log_Level value)
+{
+    switch (value) {
+        case TOX_LOG_LEVEL_TRACE:
+            return "TOX_LOG_LEVEL_TRACE";
+
+        case TOX_LOG_LEVEL_DEBUG:
+            return "TOX_LOG_LEVEL_DEBUG";
+
+        case TOX_LOG_LEVEL_INFO:
+            return "TOX_LOG_LEVEL_INFO";
+
+        case TOX_LOG_LEVEL_WARNING:
+            return "TOX_LOG_LEVEL_WARNING";
+
+        case TOX_LOG_LEVEL_ERROR:
+            return "TOX_LOG_LEVEL_ERROR";
+    }
+
+    return "<invalid Tox_Log_Level>";
+}
diff --git a/toxcore/tox_log_level.h b/toxcore/tox_log_level.h
new file mode 100644
index 00000000000..b10958da753
--- /dev/null
+++ b/toxcore/tox_log_level.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2024 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_LOG_LEVEL_H
+#define C_TOXCORE_TOXCORE_TOX_LOG_LEVEL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Severity level of log messages.
+ */
+typedef enum Tox_Log_Level {
+
+    /**
+     * Very detailed traces including all network activity.
+     */
+    TOX_LOG_LEVEL_TRACE,
+
+    /**
+     * Debug messages such as which port we bind to.
+     */
+    TOX_LOG_LEVEL_DEBUG,
+
+    /**
+     * Informational log messages such as video call status changes.
+     */
+    TOX_LOG_LEVEL_INFO,
+
+    /**
+     * Warnings about internal inconsistency or logic errors.
+     */
+    TOX_LOG_LEVEL_WARNING,
+
+    /**
+     * Severe unexpected errors caused by external or internal inconsistency.
+     */
+    TOX_LOG_LEVEL_ERROR,
+
+} Tox_Log_Level;
+
+const char *tox_log_level_to_string(Tox_Log_Level value);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_LOG_LEVEL_H */
diff --git a/toxcore/tox_memory.c b/toxcore/tox_memory.c
new file mode 100644
index 00000000000..9410094145a
--- /dev/null
+++ b/toxcore/tox_memory.c
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2016-2018 The TokTok team.
+ * Copyright © 2013 Tox project.
+ */
+#include "tox_memory.h"
+
+#include <string.h>
+
+#include "ccompat.h"
+#include "tox_memory_impl.h"
+
+Tox_Memory *tox_memory_new(const Tox_Memory_Funcs *funcs, void *user_data)
+{
+    const Tox_Memory bootstrap = {funcs, user_data};
+
+    Tox_Memory *mem = (Tox_Memory *)tox_memory_alloc(&bootstrap, sizeof(Tox_Memory));
+
+    if (mem == nullptr) {
+        return nullptr;
+    }
+
+    *mem = bootstrap;
+
+    return mem;
+}
+
+void tox_memory_free(Tox_Memory *mem)
+{
+    if (mem == nullptr) {
+        return;
+    }
+
+    tox_memory_dealloc(mem, mem);
+}
+
+void *tox_memory_malloc(const Tox_Memory *mem, uint32_t size)
+{
+    void *const ptr = mem->funcs->malloc_callback(mem->user_data, size);
+    return ptr;
+}
+
+void *tox_memory_alloc(const Tox_Memory *mem, uint32_t size)
+{
+    void *const ptr = tox_memory_malloc(mem, size);
+    if (ptr != nullptr) {
+        memset(ptr, 0, size);
+    }
+    return ptr;
+}
+
+void *tox_memory_realloc(const Tox_Memory *mem, void *ptr, uint32_t size)
+{
+    void *const new_ptr = mem->funcs->realloc_callback(mem->user_data, ptr, size);
+    return new_ptr;
+}
+
+void tox_memory_dealloc(const Tox_Memory *mem, void *ptr)
+{
+    mem->funcs->dealloc_callback(mem->user_data, ptr);
+}
diff --git a/toxcore/tox_memory.h b/toxcore/tox_memory.h
new file mode 100644
index 00000000000..b226709128d
--- /dev/null
+++ b/toxcore/tox_memory.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2016-2018 The TokTok team.
+ * Copyright © 2013 Tox project.
+ */
+
+/**
+ * Memory allocation and deallocation functions.
+ */
+#ifndef C_TOXCORE_TOXCORE_TOX_MEMORY_H
+#define C_TOXCORE_TOXCORE_TOX_MEMORY_H
+
+#include <stdint.h>     // uint*_t
+
+#include "tox_attributes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Functions wrapping standard C memory allocation functions. */
+typedef struct Tox_Memory_Funcs Tox_Memory_Funcs;
+
+/**
+ * @brief A dynamic memory allocator.
+ */
+#ifndef TOX_MEMORY_DEFINED
+#define TOX_MEMORY_DEFINED
+typedef struct Tox_Memory Tox_Memory;
+#endif
+
+/**
+ * @brief Allocates a new allocator using itself to allocate its own memory.
+ *
+ * The passed `user_data` is stored and passed to allocator callbacks. It must
+ * outlive the `Tox_Memory` object, since it may be used by the callback invoked
+ * in `tox_memory_free`.
+ *
+ * @return NULL if allocation fails.
+ */
+tox_non_null(1) tox_nullable(2)
+Tox_Memory *tox_memory_new(const Tox_Memory_Funcs *funcs, void *user_data);
+
+/**
+ * @brief Destroys the allocator using its own deallocation function.
+ *
+ * The stored `user_data` will not be deallocated.
+ */
+tox_nullable(1)
+void tox_memory_free(Tox_Memory *mem);
+
+/**
+ * @brief Allocate an array of a given size for built-in types.
+ *
+ * The array will not be initialised. Supported built-in types are
+ * `uint8_t`, `int8_t`, and `int16_t`.
+ */
+tox_non_null()
+void *tox_memory_malloc(const Tox_Memory *mem, uint32_t size);
+
+/**
+ * @brief Allocate a single zero-initialised object.
+ *
+ * Always use as `(T *)tox_memory_alloc(mem, sizeof(T))`. Unlike `calloc`, this
+ * does not support allocating arrays. Use `malloc` and `memset` for that.
+ *
+ * @param mem The memory allocator.
+ * @param size Size in bytes of each element.
+ */
+tox_non_null()
+void *tox_memory_alloc(const Tox_Memory *mem, uint32_t size);
+
+/** @brief Resize a memory chunk vector. */
+tox_non_null(1) tox_nullable(2)
+void *tox_memory_realloc(const Tox_Memory *mem, void *ptr, uint32_t size);
+
+/** @brief Free an array, object, or object vector. */
+tox_non_null(1) tox_nullable(2)
+void tox_memory_dealloc(const Tox_Memory *mem, void *ptr);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif  /* C_TOXCORE_TOXCORE_TOX_MEMORY_H */
diff --git a/toxcore/tox_memory_impl.h b/toxcore/tox_memory_impl.h
new file mode 100644
index 00000000000..e0b99619624
--- /dev/null
+++ b/toxcore/tox_memory_impl.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2016-2018 The TokTok team.
+ * Copyright © 2013 Tox project.
+ */
+
+/**
+ * Datatypes, functions and includes for the core networking.
+ */
+#ifndef C_TOXCORE_TOXCORE_TOX_MEMORY_IMPL_H
+#define C_TOXCORE_TOXCORE_TOX_MEMORY_IMPL_H
+
+#include <stdint.h>     // uint*_t
+
+#include "tox_memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Allocate a byte array, similar to malloc. */
+typedef void *tox_memory_malloc_cb(void *self, uint32_t size);
+/** @brief Reallocate a byte array, similar to realloc. */
+typedef void *tox_memory_realloc_cb(void *self, void *ptr, uint32_t size);
+/**
+ * @brief Deallocate a byte or object array, similar to free.
+ *
+ * Note that `tox_memory_free` will use this callback to deallocate itself, so
+ * once the deallocation is done, the allocator data structures can no longer be
+ * referenced.
+ */
+typedef void tox_memory_dealloc_cb(void *self, void *ptr);
+
+/** @brief Functions wrapping standard C memory allocation functions. */
+struct Tox_Memory_Funcs {
+    tox_memory_malloc_cb *malloc_callback;
+    tox_memory_realloc_cb *realloc_callback;
+    tox_memory_dealloc_cb *dealloc_callback;
+};
+
+struct Tox_Memory {
+    const Tox_Memory_Funcs *funcs;
+    void *user_data;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif  /* C_TOXCORE_TOXCORE_TOX_MEMORY_IMPL_H */
diff --git a/toxcore/tox_network.c b/toxcore/tox_network.c
new file mode 100644
index 00000000000..ac0e49bcf42
--- /dev/null
+++ b/toxcore/tox_network.c
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "tox_network.h"
+
+#include "ccompat.h"
+#include "tox_memory.h"
+#include "tox_network_impl.h"
+
+Tox_Network *tox_network_new(const Tox_Network_Funcs *funcs, void *user_data, const Tox_Memory *mem)
+{
+    Tox_Network *ns = (Tox_Network *)tox_memory_alloc(mem, sizeof(Tox_Network));
+
+    if (ns == nullptr) {
+        return nullptr;
+    }
+
+    ns->funcs = funcs;
+    ns->user_data = user_data;
+
+    ns->mem = mem;
+
+    return ns;
+}
+
+void tox_network_free(Tox_Network *ns)
+{
+    if (ns == nullptr || ns->mem == nullptr) {
+        return;
+    }
+    tox_memory_dealloc(ns->mem, ns);
+}
+
+int tox_network_close(const Tox_Network *ns, Socket sock)
+{
+    return ns->funcs->close_callback(ns->user_data, sock);
+}
+
+Socket tox_network_accept(const Tox_Network *ns, Socket sock)
+{
+    return ns->funcs->accept_callback(ns->user_data, sock);
+}
+
+int tox_network_bind(const Tox_Network *ns, Socket sock, const Network_Addr *addr)
+{
+    return ns->funcs->bind_callback(ns->user_data, sock, addr);
+}
+
+int tox_network_listen(const Tox_Network *ns, Socket sock, int backlog)
+{
+    return ns->funcs->listen_callback(ns->user_data, sock, backlog);
+}
+
+int tox_network_connect(const Tox_Network *ns, Socket sock, const Network_Addr *addr)
+{
+    return ns->funcs->connect_callback(ns->user_data, sock, addr);
+}
+
+int tox_network_recvbuf(const Tox_Network *ns, Socket sock)
+{
+    return ns->funcs->recvbuf_callback(ns->user_data, sock);
+}
+
+int tox_network_recv(const Tox_Network *ns, Socket sock, uint8_t *buf, size_t len)
+{
+    return ns->funcs->recv_callback(ns->user_data, sock, buf, len);
+}
+
+int tox_network_recvfrom(const Tox_Network *ns, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr)
+{
+    return ns->funcs->recvfrom_callback(ns->user_data, sock, buf, len, addr);
+}
+
+int tox_network_send(const Tox_Network *ns, Socket sock, const uint8_t *buf, size_t len)
+{
+    return ns->funcs->send_callback(ns->user_data, sock, buf, len);
+}
+
+int tox_network_sendto(const Tox_Network *ns, Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr)
+{
+    return ns->funcs->sendto_callback(ns->user_data, sock, buf, len, addr);
+}
+
+Socket tox_network_socket(const Tox_Network *ns, int domain, int type, int proto)
+{
+    return ns->funcs->socket_callback(ns->user_data, domain, type, proto);
+}
+
+int tox_network_socket_nonblock(const Tox_Network *ns, Socket sock, bool nonblock)
+{
+    return ns->funcs->socket_nonblock_callback(ns->user_data, sock, nonblock);
+}
+
+int tox_network_getsockopt(const Tox_Network *ns, Socket sock, int level, int optname, void *optval, size_t *optlen)
+{
+    return ns->funcs->getsockopt_callback(ns->user_data, sock, level, optname, optval, optlen);
+}
+
+int tox_network_setsockopt(const Tox_Network *ns, Socket sock, int level, int optname, const void *optval, size_t optlen)
+{
+    return ns->funcs->setsockopt_callback(ns->user_data, sock, level, optname, optval, optlen);
+}
+
+int tox_network_getaddrinfo(const Tox_Network *ns, const Tox_Memory *mem, const char *address, int family, int protocol, Network_Addr **addrs)
+{
+    return ns->funcs->getaddrinfo_callback(ns->user_data, mem, address, family, protocol, addrs);
+}
+
+int tox_network_freeaddrinfo(const Tox_Network *ns, const Tox_Memory *mem, Network_Addr *addrs)
+{
+    return ns->funcs->freeaddrinfo_callback(ns->user_data, mem, addrs);
+}
diff --git a/toxcore/tox_network.h b/toxcore/tox_network.h
new file mode 100644
index 00000000000..a2b96e30f46
--- /dev/null
+++ b/toxcore/tox_network.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_NETWORK_H
+#define C_TOXCORE_TOXCORE_TOX_NETWORK_H
+
+#include <stdbool.h>
+#include <stddef.h>  // size_t
+
+#include "tox_attributes.h"
+#include "tox_memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Tox_Network_Funcs Tox_Network_Funcs;
+
+typedef struct Tox_Network Tox_Network;
+
+tox_non_null(1, 3) tox_nullable(2)
+Tox_Network *tox_network_new(const Tox_Network_Funcs *funcs, void *user_data, const Tox_Memory *mem);
+
+tox_nullable(1)
+void tox_network_free(Tox_Network *ns);
+
+/**
+ * @brief Wrapper for sockaddr_storage and size.
+ */
+typedef struct Network_Addr Network_Addr;
+
+typedef tox_bitwise int Socket_Value;
+typedef struct Socket {
+    Socket_Value value;
+} Socket;
+
+int net_socket_to_native(Socket sock);
+Socket net_socket_from_native(int sock);
+
+tox_non_null()
+int tox_network_close(const Tox_Network *ns, Socket sock);
+tox_non_null()
+Socket tox_network_accept(const Tox_Network *ns, Socket sock);
+tox_non_null()
+int tox_network_bind(const Tox_Network *ns, Socket sock, const Network_Addr *addr);
+tox_non_null()
+int tox_network_listen(const Tox_Network *ns, Socket sock, int backlog);
+tox_non_null()
+int tox_network_connect(const Tox_Network *ns, Socket sock, const Network_Addr *addr);
+tox_non_null()
+int tox_network_recvbuf(const Tox_Network *ns, Socket sock);
+tox_non_null()
+int tox_network_recv(const Tox_Network *ns, Socket sock, uint8_t *buf, size_t len);
+tox_non_null()
+int tox_network_recvfrom(const Tox_Network *ns, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr);
+tox_non_null()
+int tox_network_send(const Tox_Network *ns, Socket sock, const uint8_t *buf, size_t len);
+tox_non_null()
+int tox_network_sendto(const Tox_Network *ns, Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr);
+tox_non_null()
+Socket tox_network_socket(const Tox_Network *ns, int domain, int type, int proto);
+tox_non_null()
+int tox_network_socket_nonblock(const Tox_Network *ns, Socket sock, bool nonblock);
+tox_non_null()
+int tox_network_getsockopt(const Tox_Network *ns, Socket sock, int level, int optname, void *optval, size_t *optlen);
+tox_non_null()
+int tox_network_setsockopt(const Tox_Network *ns, Socket sock, int level, int optname, const void *optval, size_t optlen);
+tox_non_null()
+int tox_network_getaddrinfo(const Tox_Network *ns, const Tox_Memory *mem, const char *address, int family, int protocol, Network_Addr **addrs);
+tox_non_null()
+int tox_network_freeaddrinfo(const Tox_Network *ns, const Tox_Memory *mem, Network_Addr *addrs);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_NETWORK_H */
diff --git a/toxcore/tox_network_impl.h b/toxcore/tox_network_impl.h
new file mode 100644
index 00000000000..4f8b0ca7340
--- /dev/null
+++ b/toxcore/tox_network_impl.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_NETWORK_IMPL_H
+#define C_TOXCORE_TOXCORE_TOX_NETWORK_IMPL_H
+
+#include "tox_memory.h"
+#include "tox_network.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int tox_network_close_cb(void *self, Socket sock);
+typedef Socket tox_network_accept_cb(void *self, Socket sock);
+typedef int tox_network_bind_cb(void *self, Socket sock, const Network_Addr *addr);
+typedef int tox_network_listen_cb(void *self, Socket sock, int backlog);
+typedef int tox_network_connect_cb(void *self, Socket sock, const Network_Addr *addr);
+typedef int tox_network_recvbuf_cb(void *self, Socket sock);
+typedef int tox_network_recv_cb(void *self, Socket sock, uint8_t *buf, size_t len);
+typedef int tox_network_recvfrom_cb(void *self, Socket sock, uint8_t *buf, size_t len, Network_Addr *addr);
+typedef int tox_network_send_cb(void *self, Socket sock, const uint8_t *buf, size_t len);
+typedef int tox_network_sendto_cb(void *self, Socket sock, const uint8_t *buf, size_t len, const Network_Addr *addr);
+typedef Socket tox_network_socket_cb(void *self, int domain, int type, int proto);
+typedef int tox_network_socket_nonblock_cb(void *self, Socket sock, bool nonblock);
+typedef int tox_network_getsockopt_cb(void *self, Socket sock, int level, int optname, void *optval, size_t *optlen);
+typedef int tox_network_setsockopt_cb(void *self, Socket sock, int level, int optname, const void *optval, size_t optlen);
+typedef int tox_network_getaddrinfo_cb(void *obj, const Tox_Memory *mem, const char *address, int family, int protocol, Network_Addr **addrs);
+typedef int tox_network_freeaddrinfo_cb(void *obj, const Tox_Memory *mem, Network_Addr *addrs);
+
+/** @brief Functions wrapping POSIX network functions.
+ *
+ * Refer to POSIX man pages for documentation of what these functions are
+ * expected to do when providing alternative Network implementations.
+ */
+struct Tox_Network_Funcs {
+    tox_network_close_cb *close_callback;
+    tox_network_accept_cb *accept_callback;
+    tox_network_bind_cb *bind_callback;
+    tox_network_listen_cb *listen_callback;
+    tox_network_connect_cb *connect_callback;
+    tox_network_recvbuf_cb *recvbuf_callback;
+    tox_network_recv_cb *recv_callback;
+    tox_network_recvfrom_cb *recvfrom_callback;
+    tox_network_send_cb *send_callback;
+    tox_network_sendto_cb *sendto_callback;
+    tox_network_socket_cb *socket_callback;
+    tox_network_socket_nonblock_cb *socket_nonblock_callback;
+    tox_network_getsockopt_cb *getsockopt_callback;
+    tox_network_setsockopt_cb *setsockopt_callback;
+    tox_network_getaddrinfo_cb *getaddrinfo_callback;
+    tox_network_freeaddrinfo_cb *freeaddrinfo_callback;
+};
+
+struct Tox_Network {
+    const Tox_Network_Funcs *funcs;
+    void *user_data;
+
+    const Tox_Memory *mem;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_NETWORK_IMPL_H */
diff --git a/toxcore/tox_options.c b/toxcore/tox_options.c
new file mode 100644
index 00000000000..1ccc37c6acc
--- /dev/null
+++ b/toxcore/tox_options.c
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2016-2024 The TokTok team.
+ */
+#include "tox_options.h"
+
+#include <stdlib.h>
+
+#include "ccompat.h"
+#include "tox.h"
+#include "tox_system.h"
+
+#define SET_ERROR_PARAMETER(param, x) \
+    do {                              \
+        if (param != nullptr) {       \
+            *param = x;               \
+        }                             \
+    } while (0)
+
+bool tox_options_get_ipv6_enabled(const Tox_Options *options)
+{
+    return options->ipv6_enabled;
+}
+void tox_options_set_ipv6_enabled(Tox_Options *options, bool ipv6_enabled)
+{
+    options->ipv6_enabled = ipv6_enabled;
+}
+bool tox_options_get_udp_enabled(const Tox_Options *options)
+{
+    return options->udp_enabled;
+}
+void tox_options_set_udp_enabled(Tox_Options *options, bool udp_enabled)
+{
+    options->udp_enabled = udp_enabled;
+}
+Tox_Proxy_Type tox_options_get_proxy_type(const Tox_Options *options)
+{
+    return options->proxy_type;
+}
+void tox_options_set_proxy_type(Tox_Options *options, Tox_Proxy_Type proxy_type)
+{
+    options->proxy_type = proxy_type;
+}
+const char *tox_options_get_proxy_host(const Tox_Options *options)
+{
+    return options->proxy_host;
+}
+void tox_options_set_proxy_host(Tox_Options *options, const char *proxy_host)
+{
+    options->proxy_host = proxy_host;
+}
+uint16_t tox_options_get_proxy_port(const Tox_Options *options)
+{
+    return options->proxy_port;
+}
+void tox_options_set_proxy_port(Tox_Options *options, uint16_t proxy_port)
+{
+    options->proxy_port = proxy_port;
+}
+uint16_t tox_options_get_start_port(const Tox_Options *options)
+{
+    return options->start_port;
+}
+void tox_options_set_start_port(Tox_Options *options, uint16_t start_port)
+{
+    options->start_port = start_port;
+}
+uint16_t tox_options_get_end_port(const Tox_Options *options)
+{
+    return options->end_port;
+}
+void tox_options_set_end_port(Tox_Options *options, uint16_t end_port)
+{
+    options->end_port = end_port;
+}
+uint16_t tox_options_get_tcp_port(const Tox_Options *options)
+{
+    return options->tcp_port;
+}
+void tox_options_set_tcp_port(Tox_Options *options, uint16_t tcp_port)
+{
+    options->tcp_port = tcp_port;
+}
+bool tox_options_get_hole_punching_enabled(const Tox_Options *options)
+{
+    return options->hole_punching_enabled;
+}
+void tox_options_set_hole_punching_enabled(Tox_Options *options, bool hole_punching_enabled)
+{
+    options->hole_punching_enabled = hole_punching_enabled;
+}
+Tox_Savedata_Type tox_options_get_savedata_type(const Tox_Options *options)
+{
+    return options->savedata_type;
+}
+void tox_options_set_savedata_type(Tox_Options *options, Tox_Savedata_Type savedata_type)
+{
+    options->savedata_type = savedata_type;
+}
+size_t tox_options_get_savedata_length(const Tox_Options *options)
+{
+    return options->savedata_length;
+}
+void tox_options_set_savedata_length(Tox_Options *options, size_t savedata_length)
+{
+    options->savedata_length = savedata_length;
+}
+tox_log_cb *tox_options_get_log_callback(const Tox_Options *options)
+{
+    return options->log_callback;
+}
+void tox_options_set_log_callback(Tox_Options *options, tox_log_cb *log_callback)
+{
+    options->log_callback = log_callback;
+}
+void *tox_options_get_log_user_data(const Tox_Options *options)
+{
+    return options->log_user_data;
+}
+void tox_options_set_log_user_data(Tox_Options *options, void *log_user_data)
+{
+    options->log_user_data = log_user_data;
+}
+bool tox_options_get_local_discovery_enabled(const Tox_Options *options)
+{
+    return options->local_discovery_enabled;
+}
+void tox_options_set_local_discovery_enabled(Tox_Options *options, bool local_discovery_enabled)
+{
+    options->local_discovery_enabled = local_discovery_enabled;
+}
+bool tox_options_get_dht_announcements_enabled(const Tox_Options *options)
+{
+    return options->dht_announcements_enabled;
+}
+void tox_options_set_dht_announcements_enabled(Tox_Options *options, bool dht_announcements_enabled)
+{
+    options->dht_announcements_enabled = dht_announcements_enabled;
+}
+bool tox_options_get_experimental_thread_safety(const Tox_Options *options)
+{
+    return options->experimental_thread_safety;
+}
+void tox_options_set_experimental_thread_safety(Tox_Options *options, bool experimental_thread_safety)
+{
+    options->experimental_thread_safety = experimental_thread_safety;
+}
+const Tox_System *tox_options_get_operating_system(const Tox_Options *options)
+{
+    return options->operating_system;
+}
+void tox_options_set_operating_system(Tox_Options *options, const Tox_System *operating_system)
+{
+    options->operating_system = operating_system;
+}
+bool tox_options_get_experimental_groups_persistence(const Tox_Options *options)
+{
+    return options->experimental_groups_persistence;
+}
+void tox_options_set_experimental_groups_persistence(
+    Tox_Options *options, bool experimental_groups_persistence)
+{
+    options->experimental_groups_persistence = experimental_groups_persistence;
+}
+bool tox_options_get_experimental_disable_dns(const Tox_Options *options)
+{
+    return options->experimental_disable_dns;
+}
+void tox_options_set_experimental_disable_dns(Tox_Options *options, bool experimental_disable_dns)
+{
+    options->experimental_disable_dns = experimental_disable_dns;
+}
+
+const uint8_t *tox_options_get_savedata_data(const Tox_Options *options)
+{
+    return options->savedata_data;
+}
+void tox_options_set_savedata_data(Tox_Options *options, const uint8_t *savedata_data, size_t length)
+{
+    options->savedata_data = savedata_data;
+    options->savedata_length = length;
+}
+
+void tox_options_default(Tox_Options *options)
+{
+    if (options != nullptr) {
+        const Tox_Options default_options = {false};
+        *options = default_options;
+        tox_options_set_ipv6_enabled(options, true);
+        tox_options_set_udp_enabled(options, true);
+        tox_options_set_proxy_type(options, TOX_PROXY_TYPE_NONE);
+        tox_options_set_hole_punching_enabled(options, true);
+        tox_options_set_local_discovery_enabled(options, true);
+        tox_options_set_dht_announcements_enabled(options, true);
+        tox_options_set_experimental_thread_safety(options, false);
+        tox_options_set_experimental_groups_persistence(options, false);
+    }
+}
+
+Tox_Options *tox_options_new(Tox_Err_Options_New *error)
+{
+    Tox_Options *options = (Tox_Options *)calloc(1, sizeof(Tox_Options));
+
+    if (options != nullptr) {
+        tox_options_default(options);
+        SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_OK);
+        return options;
+    }
+
+    SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_MALLOC);
+    return nullptr;
+}
+
+void tox_options_free(Tox_Options *options)
+{
+    free(options);
+}
diff --git a/toxcore/tox_options.h b/toxcore/tox_options.h
new file mode 100644
index 00000000000..31a8c3993fa
--- /dev/null
+++ b/toxcore/tox_options.h
@@ -0,0 +1,479 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2016-2025 The TokTok team.
+ * Copyright © 2013 Tox project.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_OPTIONS_H
+#define C_TOXCORE_TOXCORE_TOX_OPTIONS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "tox_attributes.h"
+#include "tox_log_level.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TOX_DEFINED
+#define TOX_DEFINED
+typedef struct Tox Tox;
+#endif /* TOX_DEFINED */
+
+#ifndef TOX_SYSTEM_DEFINED
+#define TOX_SYSTEM_DEFINED
+typedef struct Tox_System Tox_System;
+#endif /* TOX_SYSTEM_DEFINED */
+
+/** @{
+ * @name Startup options
+ */
+
+typedef struct Tox_Options Tox_Options;
+
+/**
+ * @brief Initialises a Tox_Options object with the default options.
+ *
+ * The result of this function is independent of the original options. All
+ * values will be overwritten, no values will be read (so it is permissible
+ * to pass an uninitialised object).
+ *
+ * If options is NULL, this function has no effect.
+ *
+ * @param options An options object to be filled with default options.
+ */
+tox_non_null()
+void tox_options_default(Tox_Options *options);
+
+typedef enum Tox_Err_Options_New {
+    /**
+     * The function returned successfully.
+     */
+    TOX_ERR_OPTIONS_NEW_OK,
+
+    /**
+     * The function failed to allocate enough memory for the options struct.
+     */
+    TOX_ERR_OPTIONS_NEW_MALLOC,
+} Tox_Err_Options_New;
+
+const char *tox_err_options_new_to_string(Tox_Err_Options_New value);
+
+
+/**
+ * @brief Allocates a new Tox_Options object and initialises it with the default
+ *   options.
+ *
+ * This function can be used to preserve long term ABI compatibility by
+ * giving the responsibility of allocation and deallocation to the Tox library.
+ *
+ * Objects returned from this function must be freed using the tox_options_free
+ * function.
+ *
+ * @return A new Tox_Options object with default options or NULL on failure.
+ */
+tox_nullable(1)
+Tox_Options *tox_options_new(Tox_Err_Options_New *error);
+
+/**
+ * @brief Releases all resources associated with an options objects.
+ *
+ * Passing a pointer that was not returned by tox_options_new results in
+ * undefined behaviour.
+ */
+tox_nullable(1)
+void tox_options_free(Tox_Options *options);
+
+/**
+ * @brief Type of proxy used to connect to TCP relays.
+ */
+typedef enum Tox_Proxy_Type {
+    /**
+     * Don't use a proxy.
+     */
+    TOX_PROXY_TYPE_NONE,
+
+    /**
+     * HTTP proxy using CONNECT.
+     */
+    TOX_PROXY_TYPE_HTTP,
+
+    /**
+     * SOCKS proxy for simple socket pipes.
+     */
+    TOX_PROXY_TYPE_SOCKS5,
+} Tox_Proxy_Type;
+
+const char *tox_proxy_type_to_string(Tox_Proxy_Type value);
+
+/**
+ * @brief Type of savedata to create the Tox instance from.
+ */
+typedef enum Tox_Savedata_Type {
+    /**
+     * No savedata.
+     */
+    TOX_SAVEDATA_TYPE_NONE,
+
+    /**
+     * Savedata is one that was obtained from tox_get_savedata.
+     */
+    TOX_SAVEDATA_TYPE_TOX_SAVE,
+
+    /**
+     * Savedata is a secret key of length TOX_SECRET_KEY_SIZE.
+     */
+    TOX_SAVEDATA_TYPE_SECRET_KEY,
+} Tox_Savedata_Type;
+
+const char *tox_savedata_type_to_string(Tox_Savedata_Type value);
+
+/**
+ * @brief This event is triggered when the toxcore library logs a message.
+ *
+ * This is mostly useful for debugging. This callback can be called from any
+ * function, not just tox_iterate. This means the user data lifetime must at
+ * least extend between registering and unregistering it or tox_kill.
+ *
+ * Other toxcore modules such as toxav may concurrently call this callback at
+ * any time. Thus, user code must make sure it is equipped to handle concurrent
+ * execution, e.g. by employing appropriate mutex locking.
+ *
+ * When using the experimental_thread_safety option, no Tox API functions can
+ * be called from within the log callback.
+ *
+ * @param level The severity of the log message.
+ * @param file The source file from which the message originated.
+ * @param line The source line from which the message originated.
+ * @param func The function from which the message originated.
+ * @param message The log message.
+ * @param user_data The user data pointer passed to tox_new in options.
+ */
+typedef void tox_log_cb(Tox *tox, Tox_Log_Level level, const char *file, uint32_t line, const char *func,
+                        const char *message, void *user_data);
+
+/**
+ * @brief This struct contains all the startup options for Tox.
+ *
+ * You must tox_options_new to allocate an object of this type.
+ *
+ * WARNING: Although this struct happens to be visible in the API, it is
+ * effectively private. Do not allocate this yourself or access members
+ * directly, as it *will* break binary compatibility frequently.
+ *
+ * @deprecated The memory layout of this struct (size, alignment, and field
+ *   order) is not part of the ABI. To remain compatible, prefer to use
+ *   tox_options_new to allocate the object and accessor functions to set the
+ *   members. The struct will become opaque (i.e. the definition will become
+ *   private) in v0.3.0.
+ */
+struct Tox_Options {
+    /**
+     * The type of socket to create.
+     *
+     * If this is set to false, an IPv4 socket is created, which subsequently
+     * only allows IPv4 communication.
+     * If it is set to true, an IPv6 socket is created, allowing both IPv4 and
+     * IPv6 communication.
+     */
+    bool ipv6_enabled;
+
+    /**
+     * Enable the use of UDP communication when available.
+     *
+     * Setting this to false will force Tox to use TCP only. Communications will
+     * need to be relayed through a TCP relay node, potentially slowing them
+     * down.
+     *
+     * If a proxy is enabled, UDP will be disabled if either the Tox library or
+     * the proxy don't support proxying UDP messages.
+     */
+    bool udp_enabled;
+
+    /**
+     * Enable local network peer discovery.
+     *
+     * Disabling this will cause Tox to not look for peers on the local network.
+     */
+    bool local_discovery_enabled;
+
+    /**
+     * Enable storing DHT announcements and forwarding corresponding requests.
+     *
+     * Disabling this will cause Tox to ignore the relevant packets.
+     */
+    bool dht_announcements_enabled;
+
+    /**
+     * Pass communications through a proxy.
+     */
+    Tox_Proxy_Type proxy_type;
+
+    /**
+     * The IP address or DNS name of the proxy to be used.
+     *
+     * If used, this must be non-NULL and be a valid DNS name. The name must not
+     * exceed TOX_MAX_HOSTNAME_LENGTH characters, and be in a NUL-terminated C
+     * string format (TOX_MAX_HOSTNAME_LENGTH includes the NUL byte).
+     *
+     * This member is ignored (it can be NULL) if proxy_type is
+     * TOX_PROXY_TYPE_NONE.
+     *
+     * The data pointed at by this member is owned by the user, so must
+     * outlive the options object.
+     */
+    const char *proxy_host;
+
+    /**
+     * The port to use to connect to the proxy server.
+     *
+     * Ports must be in the range (1, 65535). The value is ignored if
+     * proxy_type is TOX_PROXY_TYPE_NONE.
+     */
+    uint16_t proxy_port;
+
+    /**
+     * The start port of the inclusive port range to attempt to use.
+     *
+     * If both start_port and end_port are 0, the default port range will be
+     * used: `[33445, 33545]`.
+     *
+     * If either start_port or end_port is 0 while the other is non-zero, the
+     * non-zero port will be the only port in the range.
+     *
+     * Having start_port > end_port will yield the same behavior as if
+     * start_port and end_port were swapped.
+     */
+    uint16_t start_port;
+
+    /**
+     * The end port of the inclusive port range to attempt to use.
+     */
+    uint16_t end_port;
+
+    /**
+     * The port to use for the TCP server (relay). If 0, the TCP server is
+     * disabled.
+     *
+     * Enabling it is not required for Tox to function properly.
+     *
+     * When enabled, your Tox instance can act as a TCP relay for other Tox
+     * instance. This leads to increased traffic, thus when writing a client
+     * it is recommended to enable TCP server only if the user has an option
+     * to disable it.
+     */
+    uint16_t tcp_port;
+
+    /**
+     * Enables or disables UDP hole-punching. (Default: enabled).
+     */
+    bool hole_punching_enabled;
+
+    /**
+     * The type of savedata to load from.
+     */
+    Tox_Savedata_Type savedata_type;
+
+    /**
+     * The savedata.
+     *
+     * The data pointed at by this member is owned by the user, so must outlive
+     * the options object.
+     */
+    const uint8_t *savedata_data;
+
+    /**
+     * The length of the savedata.
+     */
+    size_t savedata_length;
+
+    /**
+     * Logging callback for the new Tox instance.
+     */
+    tox_log_cb *log_callback;
+
+    /**
+     * User data pointer passed to the logging callback.
+     */
+    void *log_user_data;
+
+    /**
+     * These options are experimental, so avoid writing code that depends on
+     * them. Options marked "experimental" may change their behaviour or go away
+     * entirely in the future, or may be renamed to something non-experimental
+     * if they become part of the supported API.
+     */
+    /**
+     * Make public API functions thread-safe using a per-instance lock.
+     *
+     * Default: false.
+     */
+    bool experimental_thread_safety;
+
+    /**
+     * Low level operating system functionality such as send/recv, random
+     * number generation, and memory allocation.
+     */
+    const Tox_System *operating_system;
+
+    /**
+     * Enable saving DHT-based group chats to Tox save data (via
+     * `tox_get_savedata`). This format will change in the future, so don't rely
+     * on it.
+     *
+     * As an alternative, clients can save the group chat ID in client-owned
+     * savedata. Then, when the client starts, it can use `tox_group_join`
+     * with the saved chat ID to recreate the group chat.
+     *
+     * Default: false.
+     */
+    bool experimental_groups_persistence;
+
+    /**
+     * @brief Disable DNS hostname resolution.
+     *
+     * Hostnames or IP addresses are passed to the bootstrap/add_tcp_relay
+     * function and proxy host options. If disabled (this flag is true), only
+     * IP addresses are allowed.
+     *
+     * If this is set to true, the library will not attempt to resolve
+     * hostnames. This is useful for clients that want to resolve hostnames
+     * themselves and pass the resolved IP addresses to the library (e.g. in
+     * case it wants to use Tor).
+     * Passing hostnames will result in a TOX_ERR_BOOTSTRAP_BAD_HOST error if
+     * this is set to true.
+     *
+     * Default: false. May become true in the future (0.3.0).
+     */
+    bool experimental_disable_dns;
+};
+
+tox_non_null()
+bool tox_options_get_ipv6_enabled(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_ipv6_enabled(struct Tox_Options *options, bool ipv6_enabled);
+
+tox_non_null()
+bool tox_options_get_udp_enabled(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_udp_enabled(struct Tox_Options *options, bool udp_enabled);
+
+tox_non_null()
+bool tox_options_get_local_discovery_enabled(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_local_discovery_enabled(struct Tox_Options *options, bool local_discovery_enabled);
+
+tox_non_null()
+bool tox_options_get_dht_announcements_enabled(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_dht_announcements_enabled(struct Tox_Options *options, bool dht_announcements_enabled);
+
+tox_non_null()
+Tox_Proxy_Type tox_options_get_proxy_type(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_proxy_type(struct Tox_Options *options, Tox_Proxy_Type proxy_type);
+
+tox_non_null()
+const char *tox_options_get_proxy_host(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_proxy_host(struct Tox_Options *options, const char *proxy_host);
+
+tox_non_null()
+uint16_t tox_options_get_proxy_port(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_proxy_port(struct Tox_Options *options, uint16_t proxy_port);
+
+tox_non_null()
+uint16_t tox_options_get_start_port(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_start_port(struct Tox_Options *options, uint16_t start_port);
+
+tox_non_null()
+uint16_t tox_options_get_end_port(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_end_port(struct Tox_Options *options, uint16_t end_port);
+
+tox_non_null()
+uint16_t tox_options_get_tcp_port(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_tcp_port(struct Tox_Options *options, uint16_t tcp_port);
+
+tox_non_null()
+bool tox_options_get_hole_punching_enabled(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_hole_punching_enabled(struct Tox_Options *options, bool hole_punching_enabled);
+
+tox_non_null()
+Tox_Savedata_Type tox_options_get_savedata_type(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_savedata_type(struct Tox_Options *options, Tox_Savedata_Type savedata_type);
+
+tox_non_null()
+const uint8_t *tox_options_get_savedata_data(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_savedata_data(struct Tox_Options *options, const uint8_t savedata_data[], size_t length);
+
+tox_non_null()
+size_t tox_options_get_savedata_length(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_savedata_length(struct Tox_Options *options, size_t savedata_length);
+
+tox_non_null()
+tox_log_cb *tox_options_get_log_callback(const struct Tox_Options *options);
+
+tox_non_null(1) tox_nullable(2)
+void tox_options_set_log_callback(struct Tox_Options *options, tox_log_cb *log_callback);
+
+tox_non_null()
+void *tox_options_get_log_user_data(const struct Tox_Options *options);
+
+tox_non_null(1) tox_nullable(2)
+void tox_options_set_log_user_data(struct Tox_Options *options, void *log_user_data);
+
+tox_non_null()
+bool tox_options_get_experimental_thread_safety(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_experimental_thread_safety(struct Tox_Options *options, bool experimental_thread_safety);
+
+tox_non_null()
+const Tox_System *tox_options_get_operating_system(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_operating_system(struct Tox_Options *options, const Tox_System *operating_system);
+
+tox_non_null()
+bool tox_options_get_experimental_groups_persistence(const struct Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_experimental_groups_persistence(struct Tox_Options *options, bool experimental_groups_persistence);
+
+tox_non_null()
+bool tox_options_get_experimental_disable_dns(const Tox_Options *options);
+
+tox_non_null()
+void tox_options_set_experimental_disable_dns(Tox_Options *options, bool experimental_disable_dns);
+
+/** @} */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_OPTIONS_H */
diff --git a/toxcore/tox_private.c b/toxcore/tox_private.c
index d7e13ac13d7..0c9b5557a7d 100644
--- a/toxcore/tox_private.c
+++ b/toxcore/tox_private.c
@@ -12,18 +12,17 @@
 
 #include "DHT.h"
 #include "TCP_server.h"
-#include "attributes.h"
 #include "ccompat.h"
-#include "crypto_core.h"
 #include "group_chats.h"
 #include "group_common.h"
 #include "logger.h"
-#include "mem.h"
 #include "net_crypto.h"
 #include "net_profile.h"
 #include "network.h"
 #include "tox.h"
-#include "tox_struct.h"
+#include "tox_attributes.h"
+#include "tox_impl.h"
+#include "tox_system.h"
 
 #define SET_ERROR_PARAMETER(param, x) \
     do {                              \
@@ -32,32 +31,6 @@
         }                             \
     } while (0)
 
-Tox_System tox_default_system(void)
-{
-    const Tox_System sys = {
-        nullptr,  // mono_time_callback
-        nullptr,  // mono_time_user_data
-        os_random(),
-        os_network(),
-        os_memory(),
-    };
-    return sys;
-}
-
-void tox_lock(const Tox *tox)
-{
-    if (tox->mutex != nullptr) {
-        pthread_mutex_lock(tox->mutex);
-    }
-}
-
-void tox_unlock(const Tox *tox)
-{
-    if (tox->mutex != nullptr) {
-        pthread_mutex_unlock(tox->mutex);
-    }
-}
-
 void tox_callback_friend_lossy_packet_per_pktid(Tox *tox, tox_friend_lossy_packet_cb *callback, uint8_t pktid)
 {
     assert(tox != nullptr);
diff --git a/toxcore/tox_private.h b/toxcore/tox_private.h
index cc8a0856598..0d4aab12d2b 100644
--- a/toxcore/tox_private.h
+++ b/toxcore/tox_private.h
@@ -11,39 +11,17 @@
 #include <stdint.h>
 
 #include "tox.h"
+#include "tox_system.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef uint64_t tox_mono_time_cb(void *user_data);
-
-typedef struct Tox_System {
-    tox_mono_time_cb *mono_time_callback;
-    void *mono_time_user_data;
-    const struct Random *rng;
-    const struct Network *ns;
-    const struct Memory *mem;
-} Tox_System;
-
-Tox_System tox_default_system(void);
-
-const Tox_System *tox_get_system(Tox *tox);
-
-typedef struct Tox_Options_Testing {
-    const struct Tox_System *operating_system;
-} Tox_Options_Testing;
-
-typedef enum Tox_Err_New_Testing {
-    TOX_ERR_NEW_TESTING_OK,
-    TOX_ERR_NEW_TESTING_NULL,
-} Tox_Err_New_Testing;
-
-Tox *tox_new_testing(const Tox_Options *options, Tox_Err_New *error, const Tox_Options_Testing *testing, Tox_Err_New_Testing *testing_error);
-
 void tox_lock(const Tox *tox);
 void tox_unlock(const Tox *tox);
 
+const Tox_System *tox_get_system(Tox *tox);
+
 /**
  * Set the callback for the `friend_lossy_packet` event for a specific packet
  * ID. Pass NULL to unset.
diff --git a/toxcore/tox_random.c b/toxcore/tox_random.c
new file mode 100644
index 00000000000..848bd7f587a
--- /dev/null
+++ b/toxcore/tox_random.c
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "tox_random.h"
+
+#include "ccompat.h"
+#include "tox_memory.h"
+#include "tox_random_impl.h"
+
+Tox_Random *tox_random_new(const Tox_Random_Funcs *funcs, void *user_data, const Tox_Memory *mem)
+{
+    Tox_Random *rng = (Tox_Random *)tox_memory_alloc(mem, sizeof(Tox_Random));
+
+    if (rng == nullptr) {
+        return nullptr;
+    }
+
+    rng->funcs = funcs;
+    rng->user_data = user_data;
+
+    rng->mem = mem;
+
+    return rng;
+}
+
+void tox_random_free(Tox_Random *rng)
+{
+    if (rng == nullptr || rng->mem == nullptr) {
+        return;
+    }
+    tox_memory_dealloc(rng->mem, rng);
+}
+
+void tox_random_bytes(const Tox_Random *rng, uint8_t *bytes, uint32_t length)
+{
+    rng->funcs->bytes_callback(rng->user_data, bytes, length);
+}
+
+uint32_t tox_random_uniform(const Tox_Random *rng, uint32_t upper_bound)
+{
+    return rng->funcs->uniform_callback(rng->user_data, upper_bound);
+}
diff --git a/toxcore/tox_random.h b/toxcore/tox_random.h
new file mode 100644
index 00000000000..9c4dfd61d82
--- /dev/null
+++ b/toxcore/tox_random.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_RANDOM_H
+#define C_TOXCORE_TOXCORE_TOX_RANDOM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "tox_attributes.h"
+#include "tox_memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Tox_Random_Funcs Tox_Random_Funcs;
+
+typedef struct Tox_Random Tox_Random;
+
+tox_non_null(1, 3) tox_nullable(2)
+Tox_Random *tox_random_new(const Tox_Random_Funcs *funcs, void *user_data, const Tox_Memory *mem);
+
+tox_nullable(1)
+void tox_random_free(Tox_Random *rng);
+
+tox_non_null()
+void tox_random_bytes(const Tox_Random *rng, uint8_t *bytes, uint32_t length);
+tox_non_null()
+uint32_t tox_random_uniform(const Tox_Random *rng, uint32_t upper_bound);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_RANDOM_H */
diff --git a/toxcore/tox_random_impl.h b/toxcore/tox_random_impl.h
new file mode 100644
index 00000000000..2d88b91c001
--- /dev/null
+++ b/toxcore/tox_random_impl.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_RANDOM_IMPL_H
+#define C_TOXCORE_TOXCORE_TOX_RANDOM_IMPL_H
+
+#include "tox_memory.h"
+#include "tox_random.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Fill a byte array with random bytes.
+ *
+ * This is the key generator callback and as such must be a cryptographically
+ * secure pseudo-random number generator (CSPRNG). The security of Tox heavily
+ * depends on the security of this RNG.
+ */
+typedef void tox_random_bytes_cb(void *self, uint8_t *bytes, uint32_t length);
+
+/** @brief Generate a random integer between 0 and @p upper_bound.
+ *
+ * Should produce a uniform random distribution, but Tox security does not
+ * depend on this being correct. In principle, it could even be a non-CSPRNG.
+ */
+typedef uint32_t tox_random_uniform_cb(void *self, uint32_t upper_bound);
+
+/** @brief Virtual function table for Random. */
+struct Tox_Random_Funcs {
+    tox_random_bytes_cb *bytes_callback;
+    tox_random_uniform_cb *uniform_callback;
+};
+
+/** @brief Random number generator object.
+ *
+ * Can be used by test code and fuzzers to make toxcore behave in specific
+ * well-defined (non-random) ways. Production code ought to use libsodium's
+ * CSPRNG and use `os_random` below.
+ */
+struct Tox_Random {
+    const Tox_Random_Funcs *funcs;
+    void *user_data;
+
+    const Tox_Memory *mem;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_RANDOM_IMPL_H */
diff --git a/toxcore/tox_system.c b/toxcore/tox_system.c
new file mode 100644
index 00000000000..98a4b063f2e
--- /dev/null
+++ b/toxcore/tox_system.c
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "tox_system.h"
+
+#include "ccompat.h"
+#include "tox_log.h"
+#include "tox_memory.h"
+#include "tox_network.h"
+#include "tox_random.h"
+#include "tox_system_impl.h"
+#include "tox_time.h"
+
+Tox_System *tox_system_new(const Tox_Log *log, const Tox_Memory *mem, const Tox_Network *ns, const Tox_Random *rng, const Tox_Time *tm)
+{
+    Tox_System *sys = (Tox_System *)tox_memory_alloc(mem, sizeof(Tox_System));
+
+    if (sys == nullptr) {
+        return nullptr;
+    }
+
+    sys->log = log;
+    sys->mem = mem;
+    sys->ns = ns;
+    sys->rng = rng;
+    sys->tm = tm;
+
+    return sys;
+}
+
+void tox_system_free(Tox_System *sys)
+{
+    if (sys == nullptr || sys->mem == nullptr) {
+        return;
+    }
+    tox_memory_dealloc(sys->mem, sys);
+}
+
+const Tox_Log *tox_system_get_log(const Tox_System *sys)
+{
+    return sys->log;
+}
+
+const Tox_Memory *tox_system_get_memory(const Tox_System *sys)
+{
+    return sys->mem;
+}
+
+const Tox_Network *tox_system_get_network(const Tox_System *sys)
+{
+    return sys->ns;
+}
+
+const Tox_Random *tox_system_get_random(const Tox_System *sys)
+{
+    return sys->rng;
+}
+
+const Tox_Time *tox_system_get_time(const Tox_System *sys)
+{
+    return sys->tm;
+}
diff --git a/toxcore/tox_system.h b/toxcore/tox_system.h
new file mode 100644
index 00000000000..b06e24633f5
--- /dev/null
+++ b/toxcore/tox_system.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_SYSTEM_H
+#define C_TOXCORE_TOXCORE_TOX_SYSTEM_H
+
+#include "tox_attributes.h"
+#include "tox_log.h"
+#include "tox_memory.h"
+#include "tox_network.h"
+#include "tox_random.h"
+#include "tox_time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Operating system functions used by Tox.
+ *
+ * This struct is opaque and generally shouldn't be used in clients, but in
+ * combination with tox_system_impl.h, it allows tests to inject non-IO
+ * (hermetic) versions of low level network, RNG, and time keeping functions.
+ */
+typedef struct Tox_System Tox_System;
+
+tox_non_null()
+Tox_System *tox_system_new(const Tox_Log *log, const Tox_Memory *mem, const Tox_Network *ns, const Tox_Random *rng, const Tox_Time *tm);
+
+tox_nullable(1)
+void tox_system_free(Tox_System *sys);
+
+tox_non_null()
+const Tox_Log *tox_system_get_log(const Tox_System *sys);
+
+tox_non_null()
+const Tox_Memory *tox_system_get_memory(const Tox_System *sys);
+
+tox_non_null()
+const Tox_Network *tox_system_get_network(const Tox_System *sys);
+
+tox_non_null()
+const Tox_Random *tox_system_get_random(const Tox_System *sys);
+
+tox_non_null()
+const Tox_Time *tox_system_get_time(const Tox_System *sys);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_SYSTEM_H */
diff --git a/toxcore/tox_system_impl.h b/toxcore/tox_system_impl.h
new file mode 100644
index 00000000000..66bb1611e03
--- /dev/null
+++ b/toxcore/tox_system_impl.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_SYSTEM_IMPL_H
+#define C_TOXCORE_TOXCORE_TOX_SYSTEM_IMPL_H
+
+#include "tox_log.h"
+#include "tox_memory.h"
+#include "tox_network.h"
+#include "tox_random.h"
+#include "tox_system.h"
+#include "tox_time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Tox_System {
+    const Tox_Log *log;
+    const Tox_Memory *mem;
+    const Tox_Network *ns;
+    const Tox_Random *rng;
+    const Tox_Time *tm;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_SYSTEM_IMPL_H */
diff --git a/toxcore/tox_test.cc b/toxcore/tox_test.cc
index d3fe9bae8a5..b9db9025571 100644
--- a/toxcore/tox_test.cc
+++ b/toxcore/tox_test.cc
@@ -6,6 +6,8 @@
 #include <vector>
 
 #include "crypto_core.h"
+#include "os_random.h"
+#include "tox_log.h"
 #include "tox_private.h"
 
 namespace {
diff --git a/toxcore/tox_time.c b/toxcore/tox_time.c
new file mode 100644
index 00000000000..4edba891b60
--- /dev/null
+++ b/toxcore/tox_time.c
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+#include "tox_time.h"
+
+#include "ccompat.h"
+#include "tox_memory.h"
+#include "tox_time_impl.h"
+
+Tox_Time *tox_time_new(const Tox_Time_Funcs *funcs, void *user_data, const Tox_Memory *mem)
+{
+    Tox_Time *tm = (Tox_Time *)tox_memory_alloc(mem, sizeof(Tox_Time));
+
+    if (tm == nullptr) {
+        return nullptr;
+    }
+
+    tm->funcs = funcs;
+    tm->user_data = user_data;
+
+    tm->mem = mem;
+
+    return tm;
+}
+
+void tox_time_free(Tox_Time *tm)
+{
+    if (tm == nullptr || tm->mem == nullptr) {
+        return;
+    }
+    tox_memory_dealloc(tm->mem, tm);
+}
+
+uint64_t tox_time_monotonic(const Tox_Time *tm)
+{
+    return tm->funcs->monotonic_callback(tm->user_data);
+}
diff --git a/toxcore/tox_time.h b/toxcore/tox_time.h
new file mode 100644
index 00000000000..8daf97efc47
--- /dev/null
+++ b/toxcore/tox_time.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_TIME_H
+#define C_TOXCORE_TOXCORE_TOX_TIME_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "tox_attributes.h"
+#include "tox_memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Tox_Time_Funcs Tox_Time_Funcs;
+
+typedef struct Tox_Time Tox_Time;
+
+tox_non_null(1, 3) tox_nullable(2)
+Tox_Time *tox_time_new(const Tox_Time_Funcs *funcs, void *user_data, const Tox_Memory *mem);
+
+tox_nullable(1)
+void tox_time_free(Tox_Time *tm);
+
+tox_non_null()
+uint64_t tox_time_monotonic(const Tox_Time *tm);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_TIME_H */
diff --git a/toxcore/tox_time_impl.h b/toxcore/tox_time_impl.h
new file mode 100644
index 00000000000..7c0ad6a6a68
--- /dev/null
+++ b/toxcore/tox_time_impl.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#ifndef C_TOXCORE_TOXCORE_TOX_TIME_IMPL_H
+#define C_TOXCORE_TOXCORE_TOX_TIME_IMPL_H
+
+#include "tox_memory.h"
+#include "tox_time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint64_t tox_time_monotonic_cb(void *self);
+
+struct Tox_Time_Funcs {
+    tox_time_monotonic_cb *monotonic_callback;
+};
+
+struct Tox_Time {
+    const Tox_Time_Funcs *funcs;
+    void *user_data;
+
+    const Tox_Memory *mem;
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* C_TOXCORE_TOXCORE_TOX_TIME_IMPL_H */
diff --git a/toxencryptsave.h b/toxencryptsave.h
new file mode 100644
index 00000000000..f5901d3cc32
--- /dev/null
+++ b/toxencryptsave.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * Copyright © 2022-2023 The TokTok team.
+ */
+
+#include "toxencryptsave/toxencryptsave.h"
diff --git a/toxencryptsave/BUILD.bazel b/toxencryptsave/BUILD.bazel
index 917d9c007e9..b2c46a3762a 100644
--- a/toxencryptsave/BUILD.bazel
+++ b/toxencryptsave/BUILD.bazel
@@ -20,6 +20,8 @@ cc_library(
         ":defines",
         "//c-toxcore/toxcore:ccompat",
         "//c-toxcore/toxcore:crypto_core",
+        "//c-toxcore/toxcore:os_memory",
+        "//c-toxcore/toxcore:os_random",
         "@libsodium",
     ],
 )
@@ -34,6 +36,8 @@ cc_library(
     deps = [
         "//c-toxcore/toxcore:ccompat",
         "//c-toxcore/toxcore:crypto_core",
+        "//c-toxcore/toxcore:os_memory",
+        "//c-toxcore/toxcore:os_random",
         "@libsodium",
     ],
 )
diff --git a/toxencryptsave/Makefile.inc b/toxencryptsave/Makefile.inc
index 154c6744d0d..25ecdda9e3d 100644
--- a/toxencryptsave/Makefile.inc
+++ b/toxencryptsave/Makefile.inc
@@ -3,7 +3,7 @@ lib_LTLIBRARIES += libtoxencryptsave.la
 libtoxencryptsave_la_include_HEADERS = \
                         ../toxencryptsave/toxencryptsave.h
 
-libtoxencryptsave_la_includedir = $(includedir)/tox
+libtoxencryptsave_la_includedir = $(includedir)/tox/toxencryptsave
 
 libtoxencryptsave_la_SOURCES = ../toxencryptsave/toxencryptsave.h \
                         ../toxencryptsave/toxencryptsave.c \
diff --git a/toxencryptsave/toxencryptsave.c b/toxencryptsave/toxencryptsave.c
index 63bda86058d..e34eb83d350 100644
--- a/toxencryptsave/toxencryptsave.c
+++ b/toxencryptsave/toxencryptsave.c
@@ -15,6 +15,9 @@
 
 #include "../toxcore/ccompat.h"
 #include "../toxcore/crypto_core.h"
+#include "../toxcore/mem.h"
+#include "../toxcore/os_memory.h"
+#include "../toxcore/os_random.h"
 #include "defines.h"
 
 static_assert(TOX_PASS_SALT_LENGTH == crypto_pwhash_scryptsalsa208sha256_SALTBYTES,
@@ -198,9 +201,10 @@ Tox_Pass_Key *tox_pass_key_derive_with_salt(
 bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t plaintext[], size_t plaintext_len,
                           uint8_t ciphertext[], Tox_Err_Encryption *error)
 {
+    const Memory *mem = os_memory();
     const Random *rng = os_random();
 
-    if (rng == nullptr) {
+    if (mem == nullptr || rng == nullptr) {
         SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED);
         return false;
     }
@@ -231,7 +235,7 @@ bool tox_pass_key_encrypt(const Tox_Pass_Key *key, const uint8_t plaintext[], si
     ciphertext += crypto_box_NONCEBYTES;
 
     /* now encrypt */
-    const int32_t encrypted_len = encrypt_data_symmetric(os_memory(), key->key, nonce, plaintext, plaintext_len, ciphertext);
+    const int32_t encrypted_len = encrypt_data_symmetric(mem, key->key, nonce, plaintext, plaintext_len, ciphertext);
     if (encrypted_len < 0 || (size_t)encrypted_len != plaintext_len + crypto_box_MACBYTES) {
         SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED);
         return false;
@@ -291,6 +295,13 @@ bool tox_pass_encrypt(const uint8_t plaintext[], size_t plaintext_len, const uin
 bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t ciphertext[], size_t ciphertext_len,
                           uint8_t plaintext[], Tox_Err_Decryption *error)
 {
+    const Memory *mem = os_memory();
+
+    if (mem == nullptr) {
+        SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_FAILED);
+        return false;
+    }
+
     if (ciphertext_len <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
         SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
         return false;
@@ -316,7 +327,7 @@ bool tox_pass_key_decrypt(const Tox_Pass_Key *key, const uint8_t ciphertext[], s
     ciphertext += crypto_box_NONCEBYTES;
 
     /* decrypt the ciphertext */
-    const int32_t decrypted_len = decrypt_data_symmetric(os_memory(), key->key, nonce, ciphertext, decrypt_length + crypto_box_MACBYTES, plaintext);
+    const int32_t decrypted_len = decrypt_data_symmetric(mem, key->key, nonce, ciphertext, decrypt_length + crypto_box_MACBYTES, plaintext);
     if (decrypted_len < 0 || (size_t)decrypted_len != decrypt_length) {
         SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_FAILED);
         return false;
diff --git a/toxencryptsave/toxencryptsave.h b/toxencryptsave/toxencryptsave.h
index f70ff8b4d16..131bf6cb393 100644
--- a/toxencryptsave/toxencryptsave.h
+++ b/toxencryptsave/toxencryptsave.h
@@ -226,10 +226,7 @@ bool tox_pass_decrypt(const uint8_t ciphertext[], size_t ciphertext_len, const u
  * using tox_pass_key_derive or tox_pass_key_derive_with_salt and must be
  * deallocated using tox_pass_key_free.
  */
-#ifndef TOX_PASS_KEY_DEFINED
-#define TOX_PASS_KEY_DEFINED
 typedef struct Tox_Pass_Key Tox_Pass_Key;
-#endif /* TOX_PASS_KEY_DEFINED */
 
 /**
  * Deallocate a Tox_Pass_Key. This function behaves like `free()`, so NULL is an