diff --git a/.gitignore b/.gitignore
index 8c5ae2dbdc..f6a241991c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,5 @@ ansible/ci/inventory_ibmcloud.yml
 # vcpkg
 vcpkg_installed/
 vcpkg-manifest-install.log
+
+collector/lib/rust/*/target/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 15589cada1..6adaadb246 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -27,3 +27,10 @@ repos:
     rev: "v1.0.0-rc.1"
     hooks:
       - id: go-fmt
+
+  - repo: https://github.com/doublify/pre-commit-rust
+    rev: v1.0
+    hooks:
+      - id: fmt
+      - id: cargo-check
+      - id: clippy
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b9b3c4e611..039c86be72 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,8 @@
 cmake_minimum_required(VERSION 3.15)
 project(collector)
 
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
 enable_testing()
 
 add_subdirectory(collector)
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000000..e9332bb798
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+members = ["collector/lib/rust/test", "collector/lib/rust/host", "collector/lib/rust/scraper"]
+resolver = "2"
diff --git a/Makefile b/Makefile
index b5c644700e..687ffe3ff5 100644
--- a/Makefile
+++ b/Makefile
@@ -126,6 +126,7 @@ clean:
 	rm -f vcpkg-manifest-install.log
 	make -C collector clean
 	make -C integration-tests clean
+	cargo clean
 
 .PHONY: shfmt-check
 shfmt-check:
diff --git a/builder/Dockerfile b/builder/Dockerfile
index ecb01b1bec..ed1a4a38b9 100644
--- a/builder/Dockerfile
+++ b/builder/Dockerfile
@@ -45,6 +45,8 @@ RUN dnf -y update \
         which \
         # for USDT support
         systemtap-sdt-devel \
+        rust \
+        cargo \
     && dnf clean all
 
 # Build dependencies from source
diff --git a/cmake/FindRust.cmake b/cmake/FindRust.cmake
new file mode 100644
index 0000000000..25104d904a
--- /dev/null
+++ b/cmake/FindRust.cmake
@@ -0,0 +1,465 @@
+# Find the Rust toolchain and add the `add_rust_library()` API to build Rust
+# libraries.
+#
+# Copyright (C) 2020-2022 Micah Snyder.
+#
+# Author: Micah Snyder
+# To see this in a sample project, visit: https://github.com/micahsnyder/cmake-rust-demo
+#
+# Code to set the Cargo arguments was lifted from:
+# https://github.com/Devolutions/CMakeRust
+#
+# This Module defines the following variables:
+# - <program>_FOUND      - True if the program was found
+# - <program>_EXECUTABLE - path of the program
+# - <program>_VERSION    - version number of the program
+#
+# ... for the following Rust toolchain programs:
+# - cargo
+# - rustc
+# - rustup
+# - rust-gdb
+# - rust-lldb
+# - rustdoc
+# - rustfmt
+# - bindgen
+#
+# Callers can make any program mandatory by setting `<program>_REQUIRED` before
+# the call to `find_package(Rust)`
+#
+# Eg:
+# find_package(Rust REQUIRED)
+#
+# This module provides the following functions:
+# =============================================
+#
+# `add_rust_library()`
+# --------------------
+#
+# This allows a caller to create a Rust static library
+# target which you can link to with `target_link_libraries()`.
+#
+# Your Rust static library target will itself depend on the native static libs
+# you get from `rustc --crate-type staticlib --print=native-static-libs /dev/null`
+#
+# The CARGO_CMD environment variable will be set to "BUILD" so you can tell
+# it's not building the unit tests inside your (optional) `build.rs` file.
+#
+# Example `add_rust_library()` usage:
+#
+#   ```cmake
+#   add_rust_library(TARGET yourlib
+#       SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
+#       BINARY_DIRECTORY "${CMAKE_BINARY_DIR}")
+#   add_library(YourProject::yourlib ALIAS yourlib)
+#
+#   add_executable(yourexe)
+#   target_link_libraries(yourexe YourProject::yourlib)
+#   ```
+#
+# If your library has unit tests AND your library does NOT depend on your C
+# librar(ies), you can use `add_rust_library()` to build your library and unit
+# tests at the same time. Just pass `PRECOMPILE_TESTS TRUE` to add_rust_library.
+# This should make it so when you run the tests, they don't have to compile
+# during the test run.
+#
+# If your library does have C dependencies, you can still precompile the tests
+# by passing `PRECOMPILE_TESTS TRUE`, with `add_rust_test()` instead.
+# It will be slower because it will have to compile the C stuff first,
+# then compile the Rust stuff from scratch. See below.
+#
+# `add_rust_test()`
+# -----------------
+#
+# This allows a caller to run `cargo test` for a specific Rust target as a CTest
+# test.
+#
+# The CARGO_CMD environment variable will be set to "TEST" so you can tell
+# it's not building the unit tests inside your (optional) `build.rs` file.
+#
+# Example `add_rust_test()` usage:
+#
+#   ```cmake
+#   add_rust_test(NAME yourlib
+#       SOURCE_DIRECTORY "${CMAKE_SOURCE_DIR}/path/to/yourlib"
+#       BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
+#   )
+#   set_property(TEST yourlib PROPERTY ENVIRONMENT ${ENVIRONMENT})
+#   ```
+#
+# If your library has unit tests AND your library DOES depend on your C
+# libraries, you can precompile the unit tests application with some extra
+# parameters to `add_rust_test()`:
+# - `PRECOMPILE_TESTS TRUE`
+# - `DEPENDS <the CMake target name for your C library dependency>`
+# - `ENVIRONMENT <a linked list of environment vars to build the Rust lib>`
+#
+# The `DEPENDS` option is required so CMake will build the C library first.
+# The `ENVIRONMENT` option is required for use in your `build.rs` file so you
+# can tell rustc how to link to your C library.
+#
+# For example:
+#
+#   ```cmake
+#   add_rust_test(NAME yourlib
+#       SOURCE_DIRECTORY "${CMAKE_SOURCE_DIR}/yourlib"
+#       BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
+#       PRECOMPILE_TESTS TRUE
+#       DEPENDS ClamAV::libclamav
+#       ENVIRONMENT "${ENVIRONMENT}"
+#   )
+#   set_property(TEST yourlib PROPERTY ENVIRONMENT ${ENVIRONMENT})
+#   ```
+#
+# `add_rust_executable()`
+# -----------------------
+#
+# This allows a caller to create a Rust executable target.
+#
+# Example `add_rust_executable()` usage:
+#
+#   ```cmake
+#   add_rust_executable(TARGET yourexe
+#       SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+#       BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
+#   )
+#   add_executable(YourProject::yourexe ALIAS yourexe)
+#   ```
+
+if(NOT DEFINED CARGO_HOME)
+    if(WIN32)
+        set(CARGO_HOME "$ENV{USERPROFILE}/.cargo")
+    else()
+        set(CARGO_HOME "$ENV{HOME}/.cargo")
+    endif()
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+function(find_rust_program RUST_PROGRAM)
+    find_program(${RUST_PROGRAM}_EXECUTABLE ${RUST_PROGRAM}
+        HINTS "${CARGO_HOME}"
+        PATH_SUFFIXES "bin"
+    )
+
+    if(${RUST_PROGRAM}_EXECUTABLE)
+        execute_process(COMMAND "${${RUST_PROGRAM}_EXECUTABLE}" --version
+            OUTPUT_VARIABLE ${RUST_PROGRAM}_VERSION_OUTPUT
+            ERROR_VARIABLE ${RUST_PROGRAM}_VERSION_ERROR
+            RESULT_VARIABLE ${RUST_PROGRAM}_VERSION_RESULT
+        )
+
+        if(NOT ${${RUST_PROGRAM}_VERSION_RESULT} EQUAL 0)
+            message(STATUS "Rust tool `${RUST_PROGRAM}` not found: Failed to determine version.")
+            unset(${RUST_PROGRAM}_EXECUTABLE)
+        else()
+            string(REGEX
+                MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?(-nightly)?"
+                ${RUST_PROGRAM}_VERSION "${${RUST_PROGRAM}_VERSION_OUTPUT}"
+            )
+            set(${RUST_PROGRAM}_VERSION "${${RUST_PROGRAM}_VERSION}" PARENT_SCOPE)
+            message(STATUS "Rust tool `${RUST_PROGRAM}` found: ${${RUST_PROGRAM}_EXECUTABLE}, ${${RUST_PROGRAM}_VERSION}")
+        endif()
+
+        mark_as_advanced(${RUST_PROGRAM}_EXECUTABLE ${RUST_PROGRAM}_VERSION)
+    else()
+        if(${${RUST_PROGRAM}_REQUIRED})
+            message(FATAL_ERROR "Rust tool `${RUST_PROGRAM}` not found.")
+        else()
+            message(STATUS "Rust tool `${RUST_PROGRAM}` not found.")
+        endif()
+    endif()
+endfunction()
+
+function(cargo_vendor)
+    set(options)
+    set(oneValueArgs TARGET SOURCE_DIRECTORY BINARY_DIRECTORY)
+    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(NOT EXISTS ${ARGS_SOURCE_DIRECTORY}/.cargo/config.toml)
+        # Vendor the dependencies and create .cargo/config.toml
+        # Vendored dependencies will be used during the build.
+        # This will allow us to package vendored dependencies in source tarballs
+        # for online builds when we run `cpack --config CPackSourceConfig.cmake`
+        message(STATUS "Running `cargo vendor` to collect dependencies for ${ARGS_TARGET}. This may take a while if the local crates.io index needs to be updated ...")
+        make_directory(${ARGS_SOURCE_DIRECTORY}/.cargo)
+        execute_process(
+            COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" ${cargo_EXECUTABLE} vendor ".cargo/vendor"
+            WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
+            OUTPUT_VARIABLE CARGO_VENDOR_OUTPUT
+            ERROR_VARIABLE CARGO_VENDOR_ERROR
+            RESULT_VARIABLE CARGO_VENDOR_RESULT
+        )
+
+        if(NOT ${CARGO_VENDOR_RESULT} EQUAL 0)
+            message(FATAL_ERROR "Failed!\n${CARGO_VENDOR_ERROR}")
+        else()
+            message("Success!")
+        endif()
+
+        write_file(${ARGS_SOURCE_DIRECTORY}/.cargo/config.toml "
+[source.crates-io]
+replace-with = \"vendored-sources\"
+
+[source.vendored-sources]
+directory = \".cargo/vendor\"
+"
+        )
+    endif()
+endfunction()
+
+function(add_rust_executable)
+    set(options)
+    set(oneValueArgs TARGET SOURCE_DIRECTORY BINARY_DIRECTORY)
+    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(WIN32)
+        set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/${ARGS_TARGET}.exe")
+    else()
+        set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/${ARGS_TARGET}")
+    endif()
+
+    file(GLOB_RECURSE EXE_SOURCES "${ARGS_SOURCE_DIRECTORY}/*.rs")
+
+    set(MY_CARGO_ARGS ${CARGO_ARGS})
+    list(APPEND MY_CARGO_ARGS "--target-dir" ${ARGS_BINARY_DIRECTORY})
+    list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
+
+    # Build the executable.
+    add_custom_command(
+        OUTPUT "${OUTPUT}"
+        COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS}
+        WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
+        DEPENDS ${EXE_SOURCES}
+        COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with:\n\t ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
+
+    # Create a target from the build output
+    add_custom_target(${ARGS_TARGET}_target
+        DEPENDS ${OUTPUT})
+
+    # Create an executable target from custom target
+    add_custom_target(${ARGS_TARGET} ALL DEPENDS ${ARGS_TARGET}_target)
+
+    # Specify where the executable is
+    set_target_properties(${ARGS_TARGET}
+        PROPERTIES
+        IMPORTED_LOCATION "${OUTPUT}"
+    )
+
+    # Vendor the dependencies, if desired
+    if(VENDOR_DEPENDENCIES)
+        cargo_vendor(TARGET "${ARGS_TARGET}"
+            SOURCE_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
+            BINARY_DIRECTORY "${ARGS_BINARY_DIRECTORY}"
+        )
+    endif()
+endfunction()
+
+function(add_rust_library)
+    set(options)
+    set(oneValueArgs TARGET SOURCE_DIRECTORY BINARY_DIRECTORY PRECOMPILE_TESTS INCLUDE_DIRECTORY BYPRODUCTS)
+    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(WIN32)
+        set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/${ARGS_TARGET}.lib")
+    else()
+        set(OUTPUT "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}/lib${ARGS_TARGET}.a")
+    endif()
+
+    file(GLOB_RECURSE LIB_SOURCES "${ARGS_SOURCE_DIRECTORY}/*.rs")
+    list(APPEND LIB_SOURCES
+            "${ARGS_SOURCE_DIRECTORY}/build.rs"
+            "${ARGS_SOURCE_DIRECTORY}/Cargo.toml")
+
+    set(MY_CARGO_ARGS ${CARGO_ARGS})
+    if(ARGS_PRECOMPILE_TESTS)
+        list(APPEND MY_CARGO_ARGS "--tests")
+    endif()
+    list(APPEND MY_CARGO_ARGS "--target-dir" ${ARGS_BINARY_DIRECTORY})
+    list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
+
+    # Build the library and generate the c-binding
+    if("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(arm64;x86_64|x86_64;arm64)$")
+        add_custom_command(
+            OUTPUT "${OUTPUT}"
+            COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "BINDINGS_INCLUDE_DIRECTORY=${ARGS_INCLUDE_DIRECTORY}" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=\"${RUSTFLAGS}\"" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS} --target=x86_64-apple-darwin
+            COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "BINDINGS_INCLUDE_DIRECTORY=${ARGS_INCLUDE_DIRECTORY}" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=\"${RUSTFLAGS}\"" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS} --target=aarch64-apple-darwin
+            COMMAND ${CMAKE_COMMAND} -E make_directory "${ARGS_BINARY_DIRECTORY}/${RUST_COMPILER_TARGET}/${CARGO_BUILD_TYPE}"
+            COMMAND lipo ARGS -create ${ARGS_BINARY_DIRECTORY}/x86_64-apple-darwin/${CARGO_BUILD_TYPE}/lib${ARGS_TARGET}.a ${ARGS_BINARY_DIRECTORY}/aarch64-apple-darwin/${CARGO_BUILD_TYPE}/lib${ARGS_TARGET}.a -output "${OUTPUT}"
+            WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
+            BYPRODUCTS ${ARGS_BYPRODUCTS}
+            DEPENDS ${LIB_SOURCES}
+            COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with:  ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
+    else()
+        add_custom_command(
+            OUTPUT "${OUTPUT}"
+            COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=build" "BINDINGS_INCLUDE_DIRECTORY=${ARGS_INCLUDE_DIRECTORY}" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" "MAINTAINER_MODE=${MAINTAINER_MODE}" "RUSTFLAGS=\"${RUSTFLAGS}\"" ${cargo_EXECUTABLE} ARGS ${MY_CARGO_ARGS}
+            WORKING_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
+            DEPENDS ${LIB_SOURCES}
+            BYPRODUCTS ${ARGS_BYPRODUCTS}
+            COMMENT "Building ${ARGS_TARGET} in ${ARGS_BINARY_DIRECTORY} with:  ${cargo_EXECUTABLE} ${MY_CARGO_ARGS_STRING}")
+    endif()
+
+    if (ARGS_INCLUDE_DIRECTORY MATCHES "^$")
+        file(MAKE_DIRECTORY "${ARGS_INCLUDE_DIRECTORY}")
+    endif()
+
+    # Create a target from the build output
+    add_custom_target(${ARGS_TARGET}_target
+        DEPENDS ${OUTPUT})
+
+    # Create a static imported library target from custom target
+    add_library(${ARGS_TARGET} STATIC IMPORTED GLOBAL)
+    add_dependencies(${ARGS_TARGET} ${ARGS_TARGET}_target)
+    target_link_libraries(${ARGS_TARGET} INTERFACE ${RUST_NATIVE_STATIC_LIBS})
+
+    # Specify where the library is and where to find the headers
+    set_target_properties(${ARGS_TARGET}
+        PROPERTIES
+        IMPORTED_LOCATION "${OUTPUT}"
+        INTERFACE_INCLUDE_DIRECTORIES "${ARGS_SOURCE_DIRECTORY};${ARGS_BINARY_DIRECTORY};${ARGS_INCLUDE_DIRECTORY}"
+    )
+
+    # Vendor the dependencies, if desired
+    if(VENDOR_DEPENDENCIES)
+        cargo_vendor(TARGET "${ARGS_TARGET}"
+            SOURCE_DIRECTORY "${ARGS_SOURCE_DIRECTORY}"
+            BINARY_DIRECTORY "${ARGS_BINARY_DIRECTORY}")
+    endif()
+endfunction()
+
+function(add_rust_test)
+    set(options)
+    set(oneValueArgs NAME SOURCE_DIRECTORY BINARY_DIRECTORY PRECOMPILE_TESTS DEPENDS)
+    set(multiValueArgs ENVIRONMENT)
+    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    set(MY_CARGO_ARGS "test")
+
+    if(NOT "${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(arm64;x86_64|x86_64;arm64)$") # Don't specify the target for universal, we'll do that manually for each build.
+        list(APPEND MY_CARGO_ARGS "--target" ${RUST_COMPILER_TARGET})
+    endif()
+
+    if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
+        list(APPEND MY_CARGO_ARGS "--release")
+    endif()
+
+    list(APPEND MY_CARGO_ARGS "--target-dir" ${ARGS_BINARY_DIRECTORY})
+    list(JOIN MY_CARGO_ARGS " " MY_CARGO_ARGS_STRING)
+
+    if(ARGS_PRECOMPILE_TESTS)
+        list(APPEND ARGS_ENVIRONMENT "CARGO_CMD=test" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}")
+        add_custom_target(${ARGS_NAME}_tests ALL
+            COMMAND ${CMAKE_COMMAND} -E env ${ARGS_ENVIRONMENT} ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --color always --no-run
+            DEPENDS ${ARGS_DEPENDS}
+            WORKING_DIRECTORY ${ARGS_SOURCE_DIRECTORY}
+        )
+    endif()
+
+    add_test(
+        NAME ${ARGS_NAME}
+        COMMAND ${CMAKE_COMMAND} -E env "CARGO_CMD=test" "CARGO_TARGET_DIR=${ARGS_BINARY_DIRECTORY}" ${cargo_EXECUTABLE} ${MY_CARGO_ARGS} --color always
+        WORKING_DIRECTORY ${ARGS_SOURCE_DIRECTORY}
+    )
+endfunction()
+
+#
+# Cargo is the primary tool for using the Rust Toolchain to to build static
+# libs that can include other crate dependencies.
+#
+find_rust_program(cargo)
+
+# These other programs may also be useful...
+find_rust_program(rustc)
+find_rust_program(rustup)
+find_rust_program(rust-gdb)
+find_rust_program(rust-lldb)
+find_rust_program(rustdoc)
+find_rust_program(rustfmt)
+find_rust_program(bindgen)
+
+if(RUSTC_MINIMUM_REQUIRED AND rustc_VERSION VERSION_LESS RUSTC_MINIMUM_REQUIRED)
+    message(FATAL_ERROR "Your Rust toolchain is to old to build this project:
+    ${rustc_VERSION} < ${RUSTC_MINIMUM_REQUIRED}")
+endif()
+
+# Determine the native libs required to link w/ rust static libs
+# message(STATUS "Detecting native static libs for rust: ${rustc_EXECUTABLE} --crate-type staticlib --print=native-static-libs /dev/null")
+execute_process(
+    COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CMAKE_BINARY_DIR}" ${rustc_EXECUTABLE} --crate-type staticlib --print=native-static-libs /dev/null
+    OUTPUT_VARIABLE RUST_NATIVE_STATIC_LIBS_OUTPUT
+    ERROR_VARIABLE RUST_NATIVE_STATIC_LIBS_ERROR
+    RESULT_VARIABLE RUST_NATIVE_STATIC_LIBS_RESULT
+)
+string(REGEX REPLACE "\r?\n" ";" LINE_LIST "${RUST_NATIVE_STATIC_LIBS_ERROR}")
+
+foreach(LINE ${LINE_LIST})
+    # do the match on each line
+    string(REGEX MATCH "native-static-libs: .*" LINE "${LINE}")
+
+    if(NOT LINE)
+        continue()
+    endif()
+
+    string(REPLACE "native-static-libs: " "" LINE "${LINE}")
+    string(REGEX REPLACE "  " "" LINE "${LINE}")
+    string(REGEX REPLACE " " ";" LINE "${LINE}")
+
+    if(LINE)
+        message(STATUS "Rust's native static libs: ${LINE}")
+        set(RUST_NATIVE_STATIC_LIBS "${LINE}")
+        break()
+    endif()
+endforeach()
+
+if(NOT RUST_COMPILER_TARGET)
+    # Automatically determine the Rust Target Triple.
+    # Note: Users may override automatic target detection by specifying their own. Most likely needed for cross-compiling.
+    # For reference determining target platform: https://doc.rust-lang.org/nightly/rustc/platform-support.html
+    if(WIN32)
+        # For windows x86/x64, it's easy enough to guess the target.
+        if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+            set(RUST_COMPILER_TARGET "x86_64-pc-windows-msvc")
+        else()
+            set(RUST_COMPILER_TARGET "i686-pc-windows-msvc")
+        endif()
+    elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin AND "${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(arm64;x86_64|x86_64;arm64)$")
+        # Special case for Darwin because we may want to build universal binaries.
+        set(RUST_COMPILER_TARGET "universal-apple-darwin")
+    else()
+        # Determine default LLVM target triple.
+        execute_process(COMMAND ${rustc_EXECUTABLE} -vV
+            OUTPUT_VARIABLE RUSTC_VV_OUT ERROR_QUIET)
+        string(REGEX REPLACE "^.*host: ([a-zA-Z0-9_\\-]+).*" "\\1" DEFAULT_RUST_COMPILER_TARGET1 "${RUSTC_VV_OUT}")
+        string(STRIP ${DEFAULT_RUST_COMPILER_TARGET1} DEFAULT_RUST_COMPILER_TARGET)
+
+        set(RUST_COMPILER_TARGET "${DEFAULT_RUST_COMPILER_TARGET}")
+    endif()
+endif()
+
+set(CARGO_ARGS "build")
+
+if(NOT "${RUST_COMPILER_TARGET}" MATCHES "^universal-apple-darwin$")
+    # Don't specify the target for macOS universal builds, we'll do that manually for each build.
+    list(APPEND CARGO_ARGS "--target" ${RUST_COMPILER_TARGET})
+endif()
+
+set(RUSTFLAGS "")
+
+if(NOT CMAKE_BUILD_TYPE)
+    set(CARGO_BUILD_TYPE "debug")
+elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release" OR ${CMAKE_BUILD_TYPE} STREQUAL "MinSizeRel")
+    set(CARGO_BUILD_TYPE "release")
+    list(APPEND CARGO_ARGS "--release")
+elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
+    set(CARGO_BUILD_TYPE "release")
+    list(APPEND CARGO_ARGS "--release")
+    set(RUSTFLAGS "-g")
+else()
+    set(CARGO_BUILD_TYPE "debug")
+endif()
+
+find_package_handle_standard_args(Rust
+    REQUIRED_VARS cargo_EXECUTABLE
+    VERSION_VAR cargo_VERSION
+)
diff --git a/collector/CMakeLists.txt b/collector/CMakeLists.txt
index 654a896bbc..d3b05b048f 100644
--- a/collector/CMakeLists.txt
+++ b/collector/CMakeLists.txt
@@ -7,6 +7,8 @@ find_package(gRPC CONFIG REQUIRED)
 find_package(civetweb CONFIG REQUIRED)
 find_package(prometheus-cpp CONFIG REQUIRED)
 
+find_package(Rust REQUIRED)
+
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR})
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall --std=c++17 -pthread -Wno-deprecated-declarations -fno-omit-frame-pointer -rdynamic")
 
diff --git a/collector/collector.cpp b/collector/collector.cpp
index a4425b44e7..bee45c72ec 100644
--- a/collector/collector.cpp
+++ b/collector/collector.cpp
@@ -42,7 +42,9 @@ extern "C" {
 #include "HostInfo.h"
 #include "LogLevel.h"
 #include "Logging.h"
+#include "RustTest.h"
 #include "Utility.h"
+#include "host/src/lib.rs.h"
 
 static const int MAX_GRPC_CONNECTION_POLLS = 30;
 
@@ -126,6 +128,7 @@ int main(int argc, char** argv) {
   CLOG(INFO) << "Collector Version: " << GetCollectorVersion();
   CLOG(INFO) << "OS: " << host_info.GetDistro();
   CLOG(INFO) << "Kernel Version: " << host_info.GetKernelVersion().GetRelease();
+  CLOG(INFO) << "From Rust: 1 + 2 = " << collector::rust::external_add(1, 2);
 
   initialChecks();
 
diff --git a/collector/lib/CMakeLists.txt b/collector/lib/CMakeLists.txt
index 4eafcf9bd8..70349f5fc0 100644
--- a/collector/lib/CMakeLists.txt
+++ b/collector/lib/CMakeLists.txt
@@ -1,3 +1,5 @@
+add_subdirectory(rust)
+
 file(GLOB COLLECTOR_LIB_SRC_FILES
     ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/system-inspector/*.cpp
@@ -13,8 +15,14 @@ target_link_libraries(collector_lib gRPC::grpc++)
 target_link_libraries(collector_lib civetweb::civetweb-cpp)
 target_link_libraries(collector_lib yaml-cpp::yaml-cpp)
 
+target_link_libraries(collector_lib rust_test::rust_static_lib)
+target_link_libraries(collector_lib host_cxx)
+target_link_libraries(collector_lib host::rust_static_lib)
+
 target_link_libraries(collector_lib rox-proto)
 
+target_include_directories(collector_lib INTERFACE $<TARGET_PROPERTY:rust_test::rust_static_lib,INTERFACE_INCLUDE_DIRECTORIES>)
+
 if(NOT DISABLE_PROFILING)
     find_library(GPERFTOOLS_PROFILER profiler)
     find_library(GPERFTOOLS_TCMALLOC tcmalloc)
diff --git a/collector/lib/HostHeuristics.cpp b/collector/lib/HostHeuristics.cpp
index 7cc6d33127..2efc41ac54 100644
--- a/collector/lib/HostHeuristics.cpp
+++ b/collector/lib/HostHeuristics.cpp
@@ -1,6 +1,7 @@
 #include "HostHeuristics.h"
 
 #include "Logging.h"
+#include "host/src/lib.rs.h"
 
 namespace collector {
 
@@ -8,6 +9,8 @@ namespace {
 
 static const char* g_switch_collection_hint = "HINT: You may alternatively want to disable collection with collector.collectionMethod=NO_COLLECTION";
 
+using HostInfo = ::rust::Box<collector::rust::HostInfo>;
+
 class Heuristic {
  public:
   // Process the given HostInfo and CollectorConfig to adjust HostConfig as necessary.
@@ -20,38 +23,38 @@ class Heuristic {
 class CollectionHeuristic : public Heuristic {
   void Process(HostInfo& host, const CollectorConfig& config, HostConfig* hconfig) const {
     // All our probes depend on eBPF.
-    if (!host.HasEBPFSupport()) {
-      CLOG(FATAL) << host.GetDistro() << " " << host.GetKernelVersion().release
+    if (!host->has_ebpf_support()) {
+      CLOG(FATAL) << host->distro().c_str() << " " << host->kernel_version()->release().c_str()
                   << " does not support eBPF, which is a requirement for Collector.";
     }
 
-    auto kernel = host.GetKernelVersion();
+    auto kernel = host->kernel_version();
     // If we're configured to use eBPF with BTF, we try to be conservative
     // and fail instead of falling-back to ebpf.
     if (config.GetCollectionMethod() == CollectionMethod::CORE_BPF) {
-      if (!host.HasBTFSymbols()) {
+      if (!host->has_btf_symbols()) {
         CLOG(FATAL) << "Missing BTF symbols, core_bpf is not available. "
                     << "They can be provided by the kernel when configured with DEBUG_INFO_BTF, "
                     << "or as file. "
                     << g_switch_collection_hint;
       }
 
-      if (!host.HasBPFRingBufferSupport()) {
+      if (!host->has_bpf_ringbuf_support()) {
         CLOG(FATAL) << "Missing RingBuffer support, core_bpf is not available. "
                     << g_switch_collection_hint;
       }
 
-      if (!host.HasBPFTracingSupport()) {
+      if (!host->has_bpf_tracing_support()) {
         CLOG(FATAL) << "Missing BPF tracepoint support.";
       }
     }
 
     // If configured to use regular eBPF, still verify if CORE_BPF is supported.
     if (config.GetCollectionMethod() == CollectionMethod::EBPF) {
-      if (host.HasBTFSymbols() &&
-          host.HasBPFRingBufferSupport() &&
-          host.HasBPFTracingSupport() &&
-          kernel.machine != "ppc64le") {
+      if (host->has_btf_symbols() &&
+          host->has_bpf_ringbuf_support() &&
+          host->has_bpf_tracing_support() &&
+          kernel->machine() != "ppc64le") {
         CLOG(INFO) << "CORE_BPF collection method is available. "
                    << "Check the documentation to compare features of "
                    << "available collection methods.";
@@ -64,8 +67,8 @@ class DockerDesktopHeuristic : public Heuristic {
  public:
   // Docker Desktop does not support eBPF so we don't support it.
   void Process(HostInfo& host, const CollectorConfig& config, HostConfig* hconfig) const {
-    if (host.IsDockerDesktop()) {
-      CLOG(FATAL) << host.GetDistro() << " does not support eBPF.";
+    if (host->is_docker_desktop()) {
+      CLOG(FATAL) << host->distro().c_str() << " does not support eBPF.";
     }
   }
 };
@@ -73,13 +76,13 @@ class DockerDesktopHeuristic : public Heuristic {
 class PowerHeuristic : public Heuristic {
  public:
   void Process(HostInfo& host, const CollectorConfig& config, HostConfig* hconfig) const {
-    auto k = host.GetKernelVersion();
+    auto k = host->kernel_version();
 
-    if (k.machine != "ppc64le") {
+    if (k->machine() != "ppc64le") {
       return;
     }
 
-    if (k.kernel == 4 && k.major == 18 && k.build_id < 477) {
+    if (k->kernel() == 4 && k->major() == 18 && k->build_id() < 477) {
       CLOG(FATAL) << "RHEL 8.6 (kernel < 4.18.0-477) on ppc64le does not support CORE_BPF";
     }
   }
@@ -89,7 +92,7 @@ class CPUHeuristic : public Heuristic {
  public:
   // Enrich HostConfig with the number of possible CPU cores.
   void Process(HostInfo& host, const CollectorConfig& config, HostConfig* hconfig) const {
-    hconfig->SetNumPossibleCPUs(host.NumPossibleCPU());
+    hconfig->SetNumPossibleCPUs(host->num_possible_cpu());
   }
 };
 
@@ -103,7 +106,7 @@ const std::unique_ptr<Heuristic> g_host_heuristics[] = {
 }  // namespace
 
 HostConfig ProcessHostHeuristics(const CollectorConfig& config) {
-  HostInfo& host_info = HostInfo::Instance();
+  HostInfo host_info = collector::rust::host_info();
   HostConfig host_config;
   for (auto& heuristic : g_host_heuristics) {
     heuristic->Process(host_info, config, &host_config);
diff --git a/collector/lib/HostHeuristics.h b/collector/lib/HostHeuristics.h
index 1cf79eb579..4aca14baca 100644
--- a/collector/lib/HostHeuristics.h
+++ b/collector/lib/HostHeuristics.h
@@ -3,7 +3,6 @@
 
 #include "CollectorConfig.h"
 #include "HostConfig.h"
-#include "HostInfo.h"
 
 namespace collector {
 
diff --git a/collector/lib/HostInfo.cpp b/collector/lib/HostInfo.cpp
index cba0fad565..2ecdad91bb 100644
--- a/collector/lib/HostInfo.cpp
+++ b/collector/lib/HostInfo.cpp
@@ -29,6 +29,7 @@ You should have received a copy of the GNU General Public License along with thi
 #include <linux/bpf.h>
 
 #include "Logging.h"
+#include "host/src/lib.rs.h"
 
 namespace collector {
 
diff --git a/collector/lib/HostInfo.h b/collector/lib/HostInfo.h
index c7f485d809..c515898432 100644
--- a/collector/lib/HostInfo.h
+++ b/collector/lib/HostInfo.h
@@ -35,6 +35,8 @@ extern "C" {
 #include "FileSystem.h"
 #include "Logging.h"
 #include "Utility.h"
+#include "host/src/lib.rs.h"
+#include "rust/cxx.h"
 
 namespace collector {
 
@@ -92,7 +94,7 @@ struct KernelVersion {
       release = kernel_version_env;
     }
 
-    struct utsname uts_buffer {};
+    struct utsname uts_buffer{};
     if (uname(&uts_buffer) == 0) {
       if (release.empty()) {
         release = uts_buffer.release;
diff --git a/collector/lib/Utility.cpp b/collector/lib/Utility.cpp
index deb98e4364..86c2650c37 100644
--- a/collector/lib/Utility.cpp
+++ b/collector/lib/Utility.cpp
@@ -160,8 +160,8 @@ const char* GetSNIHostname() {
 }
 
 std::string GetHostname() {
-  HostInfo& info = HostInfo::Instance();
-  return info.GetHostname();
+  auto host = collector::rust::host_info();
+  return std::string(host->hostname());
 }
 
 std::vector<std::string> SplitStringView(const std::string_view sv, char delim) {
diff --git a/collector/lib/rust/CMakeLists.txt b/collector/lib/rust/CMakeLists.txt
new file mode 100644
index 0000000000..65d77761fc
--- /dev/null
+++ b/collector/lib/rust/CMakeLists.txt
@@ -0,0 +1,4 @@
+
+add_subdirectory(test)
+add_subdirectory(host)
+add_subdirectory(scraper)
diff --git a/collector/lib/rust/host/CMakeLists.txt b/collector/lib/rust/host/CMakeLists.txt
new file mode 100644
index 0000000000..4cbabdea52
--- /dev/null
+++ b/collector/lib/rust/host/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(HOST_CXX_FILE "${CMAKE_BINARY_DIR}/cxxbridge/host/src/lib.rs.cc")
+
+add_rust_library(TARGET host
+    SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+    BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
+    INCLUDE_DIRECTORY "${CMAKE_BINARY_DIR}/cxxbridge/"
+    BYPRODUCTS "${HOST_CXX_FILE}"
+)
+
+add_library(host_cxx "${HOST_CXX_FILE}")
+add_dependencies(host_cxx host)
+
+add_library(host::rust_static_lib ALIAS host)
diff --git a/collector/lib/rust/host/Cargo.lock b/collector/lib/rust/host/Cargo.lock
new file mode 100644
index 0000000000..6e87055303
--- /dev/null
+++ b/collector/lib/rust/host/Cargo.lock
@@ -0,0 +1,294 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "cxx"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23c042a0ba58aaff55299632834d1ea53ceff73d62373f62c9ae60890ad1b942"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45dc1c88d0fdac57518a9b1f6c4f4fb2aca8f3c30c0d03d7d8518b47ca0bcea6"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa7ed7d30b289e2592cc55bc2ccd89803a63c913e008e6eb59f06cddf45bb52f"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b8c465d22de46b851c04630a5fc749a26005b263632ed2e0d9cc81518ead78d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
+name = "host"
+version = "0.1.0"
+dependencies = [
+ "cxx",
+ "cxx-build",
+ "regex",
+ "uname",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.162"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "rustversion"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
+
+[[package]]
+name = "scratch"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "syn"
+version = "2.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "uname"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/collector/lib/rust/host/Cargo.toml b/collector/lib/rust/host/Cargo.toml
new file mode 100644
index 0000000000..5c1dce020b
--- /dev/null
+++ b/collector/lib/rust/host/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "host"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+cxx = "1.0"
+libbpf-rs = "0.24.8"
+libc = "0.2.164"
+log = "0.4.22"
+regex = "1.11.1"
+uname = "0.1.1"
+
+[lib]
+crate-type = ["staticlib"]
+name = "host"
+
+[build-dependencies]
+cxx-build = "1.0"
diff --git a/collector/lib/rust/host/build.rs b/collector/lib/rust/host/build.rs
new file mode 100644
index 0000000000..4f2778ee79
--- /dev/null
+++ b/collector/lib/rust/host/build.rs
@@ -0,0 +1,13 @@
+extern crate cxx_build;
+
+use cxx_build::CFG;
+
+fn main() {
+    CFG.doxygen = true;
+
+    let _builder = cxx_build::bridge("src/lib.rs");
+
+    println!("cargo::rerun-if-changed=src/lib.rs");
+    println!("cargo::rerun-if-changed=src/kernel.rs");
+    println!("cargo::rerun-if-changed=src/info.rs");
+}
diff --git a/collector/lib/rust/host/src/info.rs b/collector/lib/rust/host/src/info.rs
new file mode 100644
index 0000000000..a761579e3c
--- /dev/null
+++ b/collector/lib/rust/host/src/info.rs
@@ -0,0 +1,317 @@
+use log::{debug, info, warn};
+
+use std::io::Error;
+
+use libbpf_rs::libbpf_sys::{
+    libbpf_num_possible_cpus, libbpf_probe_bpf_map_type, libbpf_probe_bpf_prog_type,
+    BPF_MAP_TYPE_RINGBUF, BPF_PROG_TYPE_TRACING,
+};
+
+use crate::KernelVersion;
+
+use std::fs::File;
+use std::io::{prelude::*, BufReader};
+use std::path::PathBuf;
+
+const MIN_RHEL_BUILD_ID: u64 = 957;
+
+// The values are taken from efi_secureboot_mode in include/linux/efi.h
+#[derive(Default)]
+pub enum SecureBootStatus {
+    #[allow(dead_code)]
+    Enabled = 3,
+    #[allow(dead_code)]
+    Disabled = 2,
+
+    // Secure Boot seems to be disabled, but the boot loaded
+    // does not provide enough information about it,
+    // so it could be enabled without the kernel being aware.
+    #[allow(dead_code)]
+    NotDetermined = 1,
+
+    // No detection is performed yet
+    #[default]
+    Unset = 0,
+}
+
+#[derive(Default)]
+pub struct HostInfo {
+    os: String,
+    build: String,
+    distro: String,
+    hostname: String,
+    kernel: KernelVersion,
+}
+
+impl HostInfo {
+    pub fn new() -> Self {
+        let mut os = String::new();
+        let mut build = String::new();
+        let mut distro = String::new();
+        let mut hostname = String::new();
+
+        if let Ok(file) = File::open("/etc/os-release") {
+            let reader = BufReader::new(file);
+
+            for line in reader.lines() {
+                match line {
+                    Ok(s) if s.starts_with("ID") => os = s["ID ".len()..].to_owned(),
+                    Ok(s) if s.starts_with("BUILD_ID") => build = s["BUILD_ID ".len()..].to_owned(),
+                    Ok(s) if s.starts_with("PRETTY_NAME") => {
+                        distro = s["PRETTY_NAME ".len()..].to_owned()
+                    }
+                    Ok(_) => {}
+                    Err(_) => break,
+                };
+            }
+        }
+
+        let hostname_paths = vec![
+            host_path("/etc/hostname"),
+            host_path("/proc/sys/kernel/hostname"),
+        ];
+        for path in hostname_paths {
+            if let Ok(mut file) = File::open(path) {
+                if file.read_to_string(&mut hostname).is_ok() {
+                    break;
+                }
+            }
+        }
+
+        HostInfo {
+            os,
+            build,
+            distro,
+            hostname,
+            kernel: KernelVersion::from_host(),
+        }
+    }
+
+    /// Whether or not this host machine is COS
+    pub fn is_cos(&self) -> bool {
+        self.os_id().eq("cos") && !self.build_id().is_empty()
+    }
+
+    /// Whether or not this host machine is CORE-OS
+    pub fn is_coreos(&self) -> bool {
+        self.os_id().eq("coreos")
+    }
+
+    /// Whether or not this host machine is DockerDesktop
+    pub fn is_docker_desktop(&self) -> bool {
+        self.os_id().eq("Docker Desktop")
+    }
+
+    /// Whether or not this host machine is Ubuntu
+    pub fn is_ubuntu(&self) -> bool {
+        self.os_id().eq("ubuntu")
+    }
+
+    /// Whether or not this host machine is Garden Linux
+    pub fn is_garden(&self) -> bool {
+        self.distro().contains("Garden Linux")
+    }
+
+    /// Gets the KernelVersion for this Host
+    /// This must return a Box in order to maintain
+    /// FFI compatibility
+    pub fn kernel_version(&self) -> Box<KernelVersion> {
+        Box::new(self.kernel.clone())
+    }
+
+    /// Gets the ID of this host, populated from /etc/os-release
+    pub fn os_id(&self) -> String {
+        self.os.clone()
+    }
+
+    /// Gets the Build ID of this host, populated from /etc/os-release
+    pub fn build_id(&self) -> String {
+        self.build.clone()
+    }
+
+    /// Gets the distribution of this host, populated from /etc/os-release
+    pub fn distro(&self) -> String {
+        self.distro.clone()
+    }
+
+    /// Gets the hostname of this host, populated either from the
+    /// environment, or from a best-guess look at the filesystem
+    /// (/etc/hostname or /proc/sys/kernel/hostname)
+    pub fn hostname(&self) -> String {
+        self.hostname.clone()
+    }
+
+    pub fn is_rhel76(&self) -> bool {
+        let k = &self.kernel;
+        if self.os_id() == "rhel" || self.os_id() == "centos" && k.release.contains(".el7.") {
+            return (k.kernel == 3 && k.major == 10) && k.build_id >= MIN_RHEL_BUILD_ID;
+        }
+        false
+    }
+
+    pub fn is_rhel86(&self) -> bool {
+        self.os_id() == "rhel" || self.os_id() == "rhcos" && self.kernel.release.contains(".el8_6")
+    }
+
+    pub fn has_ebpf_support(&self) -> bool {
+        self.is_rhel76() || self.kernel.has_ebpf_support()
+    }
+
+    pub fn has_btf_symbols(&self) -> bool {
+        // TODO: not sure I like the layout of this, perhaps it needs a revisit
+        let locations = vec![
+            // try canonical vmlinux BTF through sysfs first
+            PathBuf::from("/sys/kernel/btf/vmlinux"),
+            // fall back to trying to find vmlinux on disk otherwise
+            PathBuf::from(format!("/boot/vmlinux-{}", self.kernel.release)),
+            PathBuf::from(format!(
+                "/lib/modules/{}/vmlinux-{}",
+                self.kernel.release, self.kernel.release
+            )),
+            PathBuf::from(format!(
+                "/lib/modules/{}/build/vmlinux",
+                self.kernel.release
+            )),
+            host_path(format!(
+                "/usr/lib/modules/{}/kernel/vmlinux",
+                self.kernel.release
+            )),
+            host_path(format!(
+                "/usr/lib/debug/boot/vmlinux-{}",
+                self.kernel.release
+            )),
+            host_path(format!(
+                "/usr/lib/debug/boot/vmlinux-{}.debug",
+                self.kernel.release
+            )),
+            host_path(format!(
+                "/usr/lib/debug/lib/modules/{}/vmlinux",
+                self.kernel.release
+            )),
+        ];
+
+        for location in locations {
+            if location.exists() {
+                debug!("BTF symbols found in {:?}", location);
+                return true;
+            }
+        }
+
+        false
+    }
+
+    pub fn is_uefi(&self) -> bool {
+        match std::fs::metadata(host_path("/sys/firmware/efi")) {
+            Ok(stat) => stat.is_dir(),
+            // TODO: do some logging with the error
+            Err(_) => false,
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn secure_boot_status(&self) -> SecureBootStatus {
+        SecureBootStatus::Unset
+    }
+
+    pub fn num_possible_cpu(&self) -> i64 {
+        (unsafe { libbpf_num_possible_cpus() }) as i64
+    }
+
+    pub fn has_bpf_tracing_support(&self) -> bool {
+        let res = unsafe { libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_TRACING, std::ptr::null()) };
+
+        if res == 0 {
+            info!(
+                "BPF tracepoint program type is not supported (errno = {:?})",
+                Error::last_os_error()
+            );
+        }
+
+        if res < 0 {
+            warn!(
+                "Unable to check for the BPF tracepoint program support. Assuming it is available"
+            );
+        }
+
+        res != 0
+    }
+
+    pub fn has_bpf_ringbuf_support(&self) -> bool {
+        let res = unsafe { libbpf_probe_bpf_map_type(BPF_MAP_TYPE_RINGBUF, std::ptr::null()) };
+
+        if res == 0 {
+            info!(
+                "BPF ringbuffer map type is not available (errno = {:?})",
+                Error::last_os_error()
+            );
+        }
+
+        if res < 0 {
+            warn!("Unable to check for the BPF ringbuffer availability. Assuming it is available.");
+        }
+
+        res != 0
+    }
+}
+
+/// Constructs a path joined from the host root (usually /host)
+fn host_path<P: Into<PathBuf>>(path: P) -> PathBuf {
+    let root = PathBuf::from(std::env::var("COLLECTOR_HOST_PATH").unwrap_or("/host".to_string()));
+    root.join(path.into())
+}
+
+/// FFI-compatible constructor for HostInfo objects
+pub fn host_info() -> Box<HostInfo> {
+    Box::new(HostInfo::new())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_is_rhel76() {
+        let mut host = HostInfo {
+            kernel: KernelVersion::new("3.10.0-957.10.1.el7.x86_64", "", ""),
+            os: "rhel".to_string(),
+            ..Default::default()
+        };
+
+        assert!(
+            host.is_rhel76(),
+            "3.10.0-957.10.1.el7.x86_64 should be RHEL 7.6"
+        );
+
+        host.kernel = KernelVersion::new("4.11.0-18.10.1.el8.x86_64", "", "");
+
+        assert!(
+            !host.is_rhel76(),
+            "4.11.0-18.10.1.el8.x86_64 shouldn't be RHEL 7.6"
+        );
+    }
+
+    #[test]
+    fn test_cos_identification() {
+        assert!(
+            HostInfo {
+                os: "cos".to_string(),
+                build: "123".to_string(),
+                ..Default::default()
+            }
+            .is_cos(),
+            "COS should be identified"
+        );
+    }
+
+    #[test]
+    fn test_ubuntu_identification() {
+        assert!(
+            HostInfo {
+                os: "ubuntu".to_string(),
+                ..Default::default()
+            }
+            .is_ubuntu(),
+            "Ubuntu should be identified"
+        );
+    }
+}
diff --git a/collector/lib/rust/host/src/kernel.rs b/collector/lib/rust/host/src/kernel.rs
new file mode 100644
index 0000000000..c62e1f6d1a
--- /dev/null
+++ b/collector/lib/rust/host/src/kernel.rs
@@ -0,0 +1,168 @@
+use regex::Regex;
+use uname::uname;
+
+#[derive(Clone, Default)]
+pub struct KernelVersion {
+    pub kernel: u64,
+    pub major: u64,
+    pub minor: u64,
+    pub build_id: u64,
+
+    pub release: String,
+    pub version: String,
+    pub machine: String,
+}
+
+impl KernelVersion {
+    pub fn new(release: &str, version: &str, machine: &str) -> Self {
+        // regex for parsing first parts of release version:
+        // ^                   -> must match start of the string
+        // (\d+)\.(\d+)\.(\d+) -> match and capture kernel, major, minor versions
+        // (-(\d+))?           -> optionally match hyphen followed by build id number
+        // .*                  -> matches the rest of the string
+        let re = Regex::new(r"^(\d+)\.(\d+)\.(\d+)(-(\d+))?.*").unwrap();
+
+        let Some(captures) = re.captures(release) else {
+            return Default::default();
+        };
+
+        let kernel = captures[1].parse::<u64>().unwrap_or_default();
+        let major = captures[2].parse::<u64>().unwrap_or_default();
+        let minor = captures[3].parse::<u64>().unwrap_or_default();
+
+        let build_id = if captures.len() > 5 {
+            captures[5].parse::<u64>().unwrap_or_default()
+        } else {
+            0
+        };
+
+        KernelVersion {
+            kernel,
+            major,
+            minor,
+            build_id,
+            release: release.to_owned(),
+            version: version.to_owned(),
+            machine: machine.to_owned(),
+        }
+    }
+
+    pub fn from_host() -> KernelVersion {
+        match uname() {
+            Ok(uname::Info {
+                release,
+                version,
+                machine,
+                ..
+            }) => KernelVersion::new(&release, &version, &machine),
+            _ => {
+                let release = std::env::var("KERNEL_VERSION").unwrap_or_default();
+                KernelVersion::new(&release, "", "")
+            }
+        }
+    }
+
+    pub fn has_ebpf_support(&self) -> bool {
+        !(self.kernel < 4 || (self.kernel == 4 && self.major < 14))
+    }
+
+    pub fn has_secure_boot_param(&self) -> bool {
+        !(self.kernel < 4 || (self.kernel == 4 && self.major < 11))
+    }
+
+    pub fn short_release(&self) -> String {
+        format!("{}.{}.{}", self.kernel, self.major, self.minor)
+    }
+
+    pub fn kernel(&self) -> u64 {
+        self.kernel
+    }
+
+    pub fn major(&self) -> u64 {
+        self.major
+    }
+
+    pub fn minor(&self) -> u64 {
+        self.minor
+    }
+
+    pub fn build_id(&self) -> u64 {
+        self.build_id
+    }
+
+    pub fn release(&self) -> String {
+        self.release.clone()
+    }
+
+    pub fn version(&self) -> String {
+        self.version.clone()
+    }
+
+    pub fn machine(&self) -> String {
+        self.machine.clone()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_has_ebpf_support() {
+        let mut v = KernelVersion {
+            kernel: 3,
+            major: 13,
+            ..Default::default()
+        };
+        assert!(!v.has_ebpf_support());
+
+        v.kernel = 4;
+        assert!(!v.has_ebpf_support());
+
+        v.kernel = 5;
+        assert!(v.has_ebpf_support());
+    }
+
+    #[test]
+    fn test_has_secure_boot_param() {
+        let mut v = KernelVersion {
+            kernel: 3,
+            major: 10,
+            ..Default::default()
+        };
+        assert!(!v.has_secure_boot_param());
+
+        v.kernel = 4;
+        assert!(!v.has_secure_boot_param());
+
+        v.kernel = 5;
+        assert!(v.has_secure_boot_param());
+    }
+
+    #[test]
+    fn test_short_release() {
+        let v = KernelVersion {
+            kernel: 9,
+            major: 10,
+            minor: 11,
+            ..Default::default()
+        };
+
+        assert_eq!("9.10.11", v.short_release());
+    }
+
+    #[test]
+    fn test_kernel_version_construction() {
+        let v = KernelVersion::new("3.10.0-957.10.1.el7.x86_64", "", "");
+        assert_eq!(3, v.kernel);
+        assert_eq!(10, v.major);
+        assert_eq!(0, v.minor);
+        assert_eq!(957, v.build_id);
+
+        let v = KernelVersion::new("not.a.version-invalid.10.1.el7.x86_64", "", "");
+        assert_eq!(0, v.kernel);
+        assert_eq!(0, v.major);
+        assert_eq!(0, v.minor);
+        assert_eq!(0, v.build_id);
+    }
+}
diff --git a/collector/lib/rust/host/src/lib.rs b/collector/lib/rust/host/src/lib.rs
new file mode 100644
index 0000000000..11fe3a7ee1
--- /dev/null
+++ b/collector/lib/rust/host/src/lib.rs
@@ -0,0 +1,56 @@
+extern crate cxx;
+extern crate libbpf_rs;
+extern crate libc;
+extern crate log;
+extern crate regex;
+extern crate uname;
+
+mod info;
+mod kernel;
+
+use crate::info::*;
+use crate::kernel::*;
+
+#[cxx::bridge(namespace = "collector::rust")]
+mod ffi {
+    extern "Rust" {
+        type KernelVersion;
+        fn has_ebpf_support(&self) -> bool;
+        fn has_secure_boot_param(&self) -> bool;
+        fn short_release(&self) -> String;
+        fn kernel(&self) -> u64;
+        fn major(&self) -> u64;
+        fn minor(&self) -> u64;
+        fn build_id(&self) -> u64;
+        fn release(&self) -> String;
+        fn version(&self) -> String;
+        fn machine(&self) -> String;
+    }
+
+    extern "Rust" {
+        type HostInfo;
+
+        fn is_cos(&self) -> bool;
+        fn kernel_version(&self) -> Box<KernelVersion>;
+        fn os_id(&self) -> String;
+        fn build_id(&self) -> String;
+        fn distro(&self) -> String;
+        fn hostname(&self) -> String;
+        fn is_coreos(&self) -> bool;
+        fn is_docker_desktop(&self) -> bool;
+        fn is_ubuntu(&self) -> bool;
+        fn is_garden(&self) -> bool;
+        fn is_rhel76(&self) -> bool;
+        fn is_rhel86(&self) -> bool;
+        fn has_ebpf_support(&self) -> bool;
+        fn has_btf_symbols(&self) -> bool;
+        fn is_uefi(&self) -> bool;
+        fn num_possible_cpu(&self) -> i64;
+        fn has_bpf_tracing_support(&self) -> bool;
+        fn has_bpf_ringbuf_support(&self) -> bool;
+    }
+
+    extern "Rust" {
+        fn host_info() -> Box<HostInfo>;
+    }
+}
diff --git a/collector/lib/rust/scraper/.gitignore b/collector/lib/rust/scraper/.gitignore
new file mode 100644
index 0000000000..4f4b4b08dc
--- /dev/null
+++ b/collector/lib/rust/scraper/.gitignore
@@ -0,0 +1,3 @@
+*.skel.rs
+*.o
+*.o.tmp
diff --git a/collector/lib/rust/scraper/CMakeLists.txt b/collector/lib/rust/scraper/CMakeLists.txt
new file mode 100644
index 0000000000..06a864e02d
--- /dev/null
+++ b/collector/lib/rust/scraper/CMakeLists.txt
@@ -0,0 +1,13 @@
+
+add_rust_library(TARGET rust_scraper
+    SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+    BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
+    INCLUDE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+)
+
+add_rust_executable(TARGET rust_scraper_exe
+    SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+    BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
+)
+
+add_library(rust_scraper::rust_static_lib ALIAS rust_scraper)
diff --git a/collector/lib/rust/scraper/Cargo.toml b/collector/lib/rust/scraper/Cargo.toml
new file mode 100644
index 0000000000..8d83f8e9a5
--- /dev/null
+++ b/collector/lib/rust/scraper/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "scraper"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["staticlib"]
+name = "scraper"
+
+[dependencies]
+anyhow = "1.0.95"
+arrayvec = "0.7.6"
+clap = { version = "4.5.23", features = ["derive"] }
+clap_derive = "4.5.18"
+cxx = "1.0.136"
+libbpf-rs = "=0.25.0-beta.1"
+
+[build-dependencies]
+bindgen = "0.71.1"
+cxx-build = "1.0.136"
+libbpf-cargo = "=0.25.0-beta.1"
+vmlinux = { version = "0.0", git = "https://github.com/libbpf/vmlinux.h.git", rev = "83a228cf37fc65f2d14e4896a04922b5ee531a94" }
diff --git a/collector/lib/rust/scraper/build.rs b/collector/lib/rust/scraper/build.rs
new file mode 100644
index 0000000000..574687813a
--- /dev/null
+++ b/collector/lib/rust/scraper/build.rs
@@ -0,0 +1,69 @@
+use libbpf_cargo::SkeletonBuilder;
+use std::io;
+use std::path::PathBuf;
+use std::{env, fs};
+
+fn make_bindings() {
+    let bindings = bindgen::Builder::default()
+        .header("src/bpf/common/bindings.h")
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+        .generate()
+        .expect("failed to generate bindings");
+
+    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+    bindings
+        .write_to_file(out_path.join("bindings.rs"))
+        .expect("failed to write bindings");
+}
+
+fn make_skeleton(src: PathBuf) {
+    let src_file = src.file_name().unwrap();
+
+    let mut dst_path: PathBuf = PathBuf::from(src_file);
+    dst_path.set_extension("skel.rs");
+
+    let base = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR must be set in build script"));
+
+    let out = base.join(&dst_path);
+
+    let mut obj_path: PathBuf = out.clone();
+    obj_path.set_extension("o");
+    obj_path = base.join(&obj_path);
+
+    SkeletonBuilder::new()
+        .source(&src)
+        .clang_args([
+            "-I",
+            vmlinux::include_path_root()
+                .join("x86_64")
+                .to_str()
+                .unwrap(),
+        ])
+        .obj(&obj_path)
+        .build_and_generate(&out)
+        .unwrap();
+}
+
+fn main() -> io::Result<()> {
+    let bpf_dir = PathBuf::from(
+        env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set in build script"),
+    )
+    .join("src")
+    .join("bpf");
+
+    for file in fs::read_dir(bpf_dir)? {
+        let entry = file?;
+        let path = entry.path();
+
+        if let Some(ext) = path.extension() {
+            if ext == "c" {
+                make_skeleton(path.clone());
+                println!("cargo:rerun-if-changed={}", path.display());
+            }
+        }
+    }
+
+    make_bindings();
+
+    Ok(())
+}
diff --git a/collector/lib/rust/scraper/src/bpf.rs b/collector/lib/rust/scraper/src/bpf.rs
new file mode 100644
index 0000000000..6ecc5873d3
--- /dev/null
+++ b/collector/lib/rust/scraper/src/bpf.rs
@@ -0,0 +1,70 @@
+use anyhow::Result;
+
+use std::io::{self, Read};
+use std::mem::MaybeUninit;
+mod bpf_kernel {
+    include!(concat!(env!("OUT_DIR"), "/bpf.bpf.skel.rs"));
+}
+
+use bpf_kernel::*;
+use libbpf_rs::skel::OpenSkel;
+use libbpf_rs::skel::SkelBuilder;
+use libbpf_rs::Iter;
+
+use crate::common;
+
+#[allow(dead_code)]
+pub struct BPFScraper {
+    link: Option<libbpf_rs::Link>,
+    debug: bool,
+    reader: Option<io::BufReader<Iter>>,
+}
+
+#[allow(dead_code)]
+impl BPFScraper {
+    pub fn new(debug: bool) -> Self {
+        Self {
+            debug,
+            link: None,
+            reader: None,
+        }
+    }
+
+    pub fn start(&mut self) -> Result<()> {
+        let mut builder = BpfSkelBuilder::default();
+        builder.obj_builder.debug(self.debug);
+
+        let mut open_object = MaybeUninit::uninit();
+        let open = builder.open(&mut open_object)?;
+
+        let skel = open.load()?;
+
+        self.link = Some(skel.progs.dump_bpf_prog.attach()?);
+
+        Ok(())
+    }
+
+    pub fn stop(&self) -> Result<()> {
+        Ok(())
+    }
+}
+
+impl IntoIterator for BPFScraper {
+    type Item = common::bpf::bpf_prog_result;
+    type IntoIter = std::vec::IntoIter<Self::Item>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        let mut buffer = vec![0u8; size_of::<common::bpf::bpf_prog_result>()];
+        let iter = Iter::new(&self.link.unwrap()).unwrap();
+        let mut r = io::BufReader::new(iter);
+        let mut v = Vec::new();
+
+        while r.read_exact(&mut buffer).is_ok() {
+            let item: common::bpf::bpf_prog_result =
+                unsafe { std::ptr::read(buffer.as_ptr() as *const _) };
+            v.push(item);
+        }
+
+        v.into_iter()
+    }
+}
diff --git a/collector/lib/rust/scraper/src/bpf/bpf.bpf.c b/collector/lib/rust/scraper/src/bpf/bpf.bpf.c
new file mode 100644
index 0000000000..2221db5909
--- /dev/null
+++ b/collector/lib/rust/scraper/src/bpf/bpf.bpf.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Red Hat, Inc. */
+#include <vmlinux.h>
+
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_helpers.h>
+
+#include "common/bpf_iterator.h"
+
+char _license[] SEC("license") = "GPL";
+
+static const char* get_name(struct btf* btf, long btf_id, const char* fallback) {
+  struct btf_type **types, *t;
+  unsigned int name_off;
+  const char* str;
+
+  if (!btf) {
+    return fallback;
+  }
+  str = btf->strings;
+  types = btf->types;
+  bpf_probe_read_kernel(&t, sizeof(t), types + btf_id);
+  name_off = BPF_CORE_READ(t, name_off);
+  if (name_off >= btf->hdr.str_len) {
+    return fallback;
+  }
+  return str + name_off;
+}
+
+SEC("iter/bpf_link")
+int dump_bpf_link(struct bpf_iter__bpf_link* ctx) {
+  struct seq_file* seq = ctx->meta->seq;
+  struct bpf_link* link = ctx->link;
+  int link_id;
+
+  if (!link) {
+    return 0;
+  }
+
+  link_id = link->id;
+  BPF_SEQ_PRINTF(seq, "%d\n", link_id);
+  return 0;
+}
+
+SEC("iter/bpf_prog")
+int dump_bpf_prog(struct bpf_iter__bpf_prog* ctx) {
+  struct seq_file* seq = ctx->meta->seq;
+  __u64 seq_num = ctx->meta->seq_num;
+  struct bpf_prog* prog = ctx->prog;
+  struct bpf_prog_aux* aux;
+
+  if (!prog) {
+    return 0;
+  }
+
+  struct bpf_prog_result result = {0};
+
+  aux = prog->aux;
+
+  result.id = aux->id;
+  bpf_core_read_str(result.name, BPF_STR_MAX, get_name(aux->btf, aux->func_info[0].type_id, aux->name));
+  bpf_core_read_str(result.attached, BPF_STR_MAX, aux->attach_func_name);
+
+  bpf_seq_write(seq, &result, sizeof(result));
+
+  return 0;
+}
diff --git a/collector/lib/rust/scraper/src/bpf/bpf_tracing_net.h b/collector/lib/rust/scraper/src/bpf/bpf_tracing_net.h
new file mode 100644
index 0000000000..b8b9bf4d4a
--- /dev/null
+++ b/collector/lib/rust/scraper/src/bpf/bpf_tracing_net.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __BPF_TRACING_NET_H__
+#define __BPF_TRACING_NET_H__
+
+#include <vmlinux.h>
+
+#include <bpf/bpf_core_read.h>
+
+#define AF_INET 2
+#define AF_INET6 10
+
+#define SOL_SOCKET 1
+#define SO_REUSEADDR 2
+#define SO_SNDBUF 7
+#define SO_RCVBUF 8
+#define SO_KEEPALIVE 9
+#define SO_PRIORITY 12
+#define SO_REUSEPORT 15
+#define SO_RCVLOWAT 18
+#define SO_BINDTODEVICE 25
+#define SO_MARK 36
+#define SO_MAX_PACING_RATE 47
+#define SO_BINDTOIFINDEX 62
+#define SO_TXREHASH 74
+#define __SO_ACCEPTCON (1 << 16)
+
+#define IP_TOS 1
+
+#define SOL_IPV6 41
+#define IPV6_TCLASS 67
+#define IPV6_AUTOFLOWLABEL 70
+
+#define TC_ACT_UNSPEC (-1)
+#define TC_ACT_OK 0
+#define TC_ACT_SHOT 2
+
+#define SOL_TCP 6
+#define TCP_NODELAY 1
+#define TCP_MAXSEG 2
+#define TCP_KEEPIDLE 4
+#define TCP_KEEPINTVL 5
+#define TCP_KEEPCNT 6
+#define TCP_SYNCNT 7
+#define TCP_WINDOW_CLAMP 10
+#define TCP_CONGESTION 13
+#define TCP_THIN_LINEAR_TIMEOUTS 16
+#define TCP_USER_TIMEOUT 18
+#define TCP_NOTSENT_LOWAT 25
+#define TCP_SAVE_SYN 27
+#define TCP_SAVED_SYN 28
+#define TCP_CA_NAME_MAX 16
+#define TCP_NAGLE_OFF 1
+
+#define TCP_ECN_OK 1
+#define TCP_ECN_QUEUE_CWR 2
+#define TCP_ECN_DEMAND_CWR 4
+#define TCP_ECN_SEEN 8
+
+#define TCP_CONG_NEEDS_ECN 0x2
+
+#define ICSK_TIME_RETRANS 1
+#define ICSK_TIME_PROBE0 3
+#define ICSK_TIME_LOSS_PROBE 5
+#define ICSK_TIME_REO_TIMEOUT 6
+
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+#define ETH_P_IP 0x0800
+#define ETH_P_IPV6 0x86DD
+
+#define NEXTHDR_TCP 6
+
+#define TCPOPT_NOP 1
+#define TCPOPT_EOL 0
+#define TCPOPT_MSS 2
+#define TCPOPT_WINDOW 3
+#define TCPOPT_TIMESTAMP 8
+#define TCPOPT_SACK_PERM 4
+
+#define TCPOLEN_MSS 4
+#define TCPOLEN_WINDOW 3
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_SACK_PERM 2
+
+#define CHECKSUM_NONE 0
+#define CHECKSUM_PARTIAL 3
+
+#define IFNAMSIZ 16
+
+#define RTF_GATEWAY 0x0002
+
+#define TCP_INFINITE_SSTHRESH 0x7fffffff
+#define TCP_PINGPONG_THRESH 3
+
+#define FLAG_DATA_ACKED 0x04  /* This ACK acknowledged new data.		*/
+#define FLAG_SYN_ACKED 0x10   /* This ACK acknowledged SYN.		*/
+#define FLAG_DATA_SACKED 0x20 /* New SACK.				*/
+#define FLAG_SND_UNA_ADVANCED \
+  0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */
+#define FLAG_ACKED (FLAG_DATA_ACKED | FLAG_SYN_ACKED)
+#define FLAG_FORWARD_PROGRESS (FLAG_ACKED | FLAG_DATA_SACKED)
+
+#define fib_nh_dev nh_common.nhc_dev
+#define fib_nh_gw_family nh_common.nhc_gw_family
+#define fib_nh_gw6 nh_common.nhc_gw.ipv6
+
+#define inet_daddr sk.__sk_common.skc_daddr
+#define inet_rcv_saddr sk.__sk_common.skc_rcv_saddr
+#define inet_dport sk.__sk_common.skc_dport
+
+#define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1]
+
+#define ir_loc_addr req.__req_common.skc_rcv_saddr
+#define ir_num req.__req_common.skc_num
+#define ir_rmt_addr req.__req_common.skc_daddr
+#define ir_rmt_port req.__req_common.skc_dport
+#define ir_v6_rmt_addr req.__req_common.skc_v6_daddr
+#define ir_v6_loc_addr req.__req_common.skc_v6_rcv_saddr
+
+#define sk_num __sk_common.skc_num
+#define sk_dport __sk_common.skc_dport
+#define sk_family __sk_common.skc_family
+#define sk_rmem_alloc sk_backlog.rmem_alloc
+#define sk_refcnt __sk_common.skc_refcnt
+#define sk_state __sk_common.skc_state
+#define sk_net __sk_common.skc_net
+#define sk_v6_daddr __sk_common.skc_v6_daddr
+#define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr
+#define sk_flags __sk_common.skc_flags
+#define sk_reuse __sk_common.skc_reuse
+#define sk_cookie __sk_common.skc_cookie
+
+#define s6_addr32 in6_u.u6_addr32
+
+#define tw_daddr __tw_common.skc_daddr
+#define tw_rcv_saddr __tw_common.skc_rcv_saddr
+#define tw_dport __tw_common.skc_dport
+#define tw_refcnt __tw_common.skc_refcnt
+#define tw_v6_daddr __tw_common.skc_v6_daddr
+#define tw_v6_rcv_saddr __tw_common.skc_v6_rcv_saddr
+
+#define tcp_jiffies32 ((__u32)bpf_jiffies64())
+
+static inline struct inet_connection_sock* inet_csk(const struct sock* sk) {
+  return (struct inet_connection_sock*)sk;
+}
+
+static inline void* inet_csk_ca(const struct sock* sk) {
+  return (void*)inet_csk(sk)->icsk_ca_priv;
+}
+
+static inline struct tcp_sock* tcp_sk(const struct sock* sk) {
+  return (struct tcp_sock*)sk;
+}
+
+static inline bool tcp_in_slow_start(const struct tcp_sock* tp) {
+  return tp->snd_cwnd < tp->snd_ssthresh;
+}
+
+static inline bool tcp_is_cwnd_limited(const struct sock* sk) {
+  const struct tcp_sock* tp = tcp_sk(sk);
+
+  /* If in slow start, ensure cwnd grows to twice what was ACKed. */
+  if (tcp_in_slow_start(tp)) {
+    return tp->snd_cwnd < 2 * tp->max_packets_out;
+  }
+
+  return !!BPF_CORE_READ_BITFIELD(tp, is_cwnd_limited);
+}
+
+#endif
diff --git a/collector/lib/rust/scraper/src/bpf/common/bindings.h b/collector/lib/rust/scraper/src/bpf/common/bindings.h
new file mode 100644
index 0000000000..087a3ccf0d
--- /dev/null
+++ b/collector/lib/rust/scraper/src/bpf/common/bindings.h
@@ -0,0 +1,6 @@
+#ifndef __SCRAPER_BINDINGS_H
+#define __SCRAPER_BINDINGS_H
+
+#include "bpf_iterator.h"
+
+#endif
diff --git a/collector/lib/rust/scraper/src/bpf/common/bpf_iterator.h b/collector/lib/rust/scraper/src/bpf/common/bpf_iterator.h
new file mode 100644
index 0000000000..2438b02e2e
--- /dev/null
+++ b/collector/lib/rust/scraper/src/bpf/common/bpf_iterator.h
@@ -0,0 +1,12 @@
+#ifndef __SCRAPER_BPF_ITER_H
+#define __SCRAPER_BPF_ITER_H
+
+#define BPF_STR_MAX 32
+
+struct bpf_prog_result {
+  unsigned int id;
+  char name[BPF_STR_MAX];
+  char attached[BPF_STR_MAX];
+};
+
+#endif
diff --git a/collector/lib/rust/scraper/src/bpf/network.bpf.c b/collector/lib/rust/scraper/src/bpf/network.bpf.c
new file mode 100644
index 0000000000..57df360d78
--- /dev/null
+++ b/collector/lib/rust/scraper/src/bpf/network.bpf.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2020 Facebook */
+#include <vmlinux.h>
+
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_helpers.h>
+
+#include "bpf_tracing_net.h"
+
+struct bpf_iter__tcp {
+  struct bpf_iter_meta* meta;
+  struct sock_common* sk_common;
+  uid_t uid;
+} __attribute__((preserve_access_index));
+
+char _license[] SEC("license") = "GPL";
+
+static int hlist_unhashed_lockless(const struct hlist_node* h) {
+  return !(h->pprev);
+}
+
+static int timer_pending(const struct timer_list* timer) {
+  return !hlist_unhashed_lockless(&timer->entry);
+}
+
+extern unsigned CONFIG_HZ __kconfig;
+
+#define USER_HZ 100
+#define NSEC_PER_SEC 1000000000ULL
+static clock_t jiffies_to_clock_t(unsigned long x) {
+  /* The implementation here tailored to a particular
+   * setting of USER_HZ.
+   */
+  u64 tick_nsec = (NSEC_PER_SEC + CONFIG_HZ / 2) / CONFIG_HZ;
+  u64 user_hz_nsec = NSEC_PER_SEC / USER_HZ;
+
+  if ((tick_nsec % user_hz_nsec) == 0) {
+    if (CONFIG_HZ < USER_HZ) {
+      return x * (USER_HZ / CONFIG_HZ);
+    } else {
+      return x / (CONFIG_HZ / USER_HZ);
+    }
+  }
+  return x * tick_nsec / user_hz_nsec;
+}
+
+static clock_t jiffies_delta_to_clock_t(long delta) {
+  if (delta <= 0) {
+    return 0;
+  }
+
+  return jiffies_to_clock_t(delta);
+}
+
+static long sock_i_ino(const struct sock* sk) {
+  const struct socket* sk_socket = sk->sk_socket;
+  const struct inode* inode;
+  unsigned long ino;
+
+  if (!sk_socket) {
+    return 0;
+  }
+
+  inode = &container_of(sk_socket, struct socket_alloc, socket)->vfs_inode;
+  bpf_probe_read_kernel(&ino, sizeof(ino), &inode->i_ino);
+  return ino;
+}
+
+static bool
+inet_csk_in_pingpong_mode(const struct inet_connection_sock* icsk) {
+  return icsk->icsk_ack.pingpong >= TCP_PINGPONG_THRESH;
+}
+
+static bool tcp_in_initial_slowstart(const struct tcp_sock* tcp) {
+  return tcp->snd_ssthresh >= TCP_INFINITE_SSTHRESH;
+}
+
+static int dump_tcp_sock(struct seq_file* seq, struct tcp_sock* tp,
+                         uid_t uid, __u32 seq_num) {
+  const struct inet_connection_sock* icsk;
+  const struct fastopen_queue* fastopenq;
+  const struct inet_sock* inet;
+  unsigned long timer_expires;
+  const struct sock* sp;
+  __u16 destp, srcp;
+  __be32 dest, src;
+  int timer_active;
+  int rx_queue;
+  int state;
+
+  icsk = &tp->inet_conn;
+  inet = &icsk->icsk_inet;
+  sp = &inet->sk;
+  fastopenq = &icsk->icsk_accept_queue.fastopenq;
+
+  dest = inet->inet_daddr;
+  src = inet->inet_rcv_saddr;
+  destp = bpf_ntohs(inet->inet_dport);
+  srcp = bpf_ntohs(inet->inet_sport);
+
+  if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
+      icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||
+      icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
+    timer_active = 1;
+    timer_expires = icsk->icsk_timeout;
+  } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
+    timer_active = 4;
+    timer_expires = icsk->icsk_timeout;
+  } else if (timer_pending(&sp->sk_timer)) {
+    timer_active = 2;
+    timer_expires = sp->sk_timer.expires;
+  } else {
+    timer_active = 0;
+    timer_expires = bpf_jiffies64();
+  }
+
+  state = sp->sk_state;
+  if (state == TCP_LISTEN) {
+    rx_queue = sp->sk_ack_backlog;
+  } else {
+    rx_queue = tp->rcv_nxt - tp->copied_seq;
+    if (rx_queue < 0) {
+      rx_queue = 0;
+    }
+  }
+
+  BPF_SEQ_PRINTF(seq, "%4d: %08X:%04X %08X:%04X ",
+                 seq_num, src, srcp, dest, destp);
+  BPF_SEQ_PRINTF(seq, "%02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d ",
+                 state,
+                 tp->write_seq - tp->snd_una, rx_queue,
+                 timer_active,
+                 jiffies_delta_to_clock_t(timer_expires - bpf_jiffies64()),
+                 icsk->icsk_retransmits, uid,
+                 icsk->icsk_probes_out,
+                 sock_i_ino(sp),
+                 sp->sk_refcnt.refs.counter);
+  BPF_SEQ_PRINTF(seq, "%pK %lu %lu %u %u %d\n",
+                 tp,
+                 jiffies_to_clock_t(icsk->icsk_rto),
+                 // jiffies_to_clock_t(icsk->icsk_ack.ato),
+                 0,
+                 (icsk->icsk_ack.quick << 1) | inet_csk_in_pingpong_mode(icsk),
+                 tp->snd_cwnd,
+                 state == TCP_LISTEN ? fastopenq->max_qlen
+                                     : (tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh));
+
+  return 0;
+}
+
+static int dump_tw_sock(struct seq_file* seq, struct tcp_timewait_sock* ttw,
+                        uid_t uid, __u32 seq_num) {
+  struct inet_timewait_sock* tw = &ttw->tw_sk;
+  __u16 destp, srcp;
+  __be32 dest, src;
+  long delta;
+
+  delta = tw->tw_timer.expires - bpf_jiffies64();
+  dest = tw->tw_daddr;
+  src = tw->tw_rcv_saddr;
+  destp = bpf_ntohs(tw->tw_dport);
+  srcp = bpf_ntohs(tw->tw_sport);
+
+  BPF_SEQ_PRINTF(seq, "%4d: %08X:%04X %08X:%04X ",
+                 seq_num, src, srcp, dest, destp);
+
+  BPF_SEQ_PRINTF(seq, "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK\n",
+                 tw->tw_substate, 0, 0,
+                 3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0,
+                 tw->tw_refcnt.refs.counter, tw);
+
+  return 0;
+}
+
+static int dump_req_sock(struct seq_file* seq, struct tcp_request_sock* treq,
+                         uid_t uid, __u32 seq_num) {
+  struct inet_request_sock* irsk = &treq->req;
+  struct request_sock* req = &irsk->req;
+  long ttd;
+
+  ttd = req->rsk_timer.expires - bpf_jiffies64();
+
+  if (ttd < 0) {
+    ttd = 0;
+  }
+
+  BPF_SEQ_PRINTF(seq, "%4d: %08X:%04X %08X:%04X ",
+                 seq_num, irsk->ir_loc_addr,
+                 irsk->ir_num, irsk->ir_rmt_addr,
+                 bpf_ntohs(irsk->ir_rmt_port));
+  BPF_SEQ_PRINTF(seq, "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK\n",
+                 TCP_SYN_RECV, 0, 0, 1, jiffies_to_clock_t(ttd),
+                 req->num_timeout, uid, 0, 0, 0, req);
+
+  return 0;
+}
+
+SEC("iter/tcp")
+int dump_tcp4(struct bpf_iter__tcp* ctx) {
+  struct sock_common* sk_common = ctx->sk_common;
+  struct seq_file* seq = ctx->meta->seq;
+  struct tcp_timewait_sock* tw;
+  struct tcp_request_sock* req;
+  struct tcp_sock* tp;
+  uid_t uid = ctx->uid;
+  __u32 seq_num;
+
+  if (sk_common == (void*)0) {
+    return 0;
+  }
+
+  seq_num = ctx->meta->seq_num;
+  // if (seq_num == 0) {
+  //   BPF_SEQ_PRINTF(seq,
+  //                  "  sl  "
+  //                  "local_address "
+  //                  "rem_address   "
+  //                  "st tx_queue rx_queue tr tm->when retrnsmt"
+  //                  "   uid  timeout inode\n");
+  // }
+
+  if (sk_common->skc_family != AF_INET) {
+    return 0;
+  }
+
+  tp = bpf_skc_to_tcp_sock(sk_common);
+  if (tp) {
+    return dump_tcp_sock(seq, tp, uid, seq_num);
+  }
+
+  tw = bpf_skc_to_tcp_timewait_sock(sk_common);
+  if (tw) {
+    return dump_tw_sock(seq, tw, uid, seq_num);
+  }
+
+  req = bpf_skc_to_tcp_request_sock(sk_common);
+  if (req) {
+    return dump_req_sock(seq, req, uid, seq_num);
+  }
+
+  return 0;
+}
diff --git a/collector/lib/rust/scraper/src/bpf/tasks.bpf.c b/collector/lib/rust/scraper/src/bpf/tasks.bpf.c
new file mode 100644
index 0000000000..071d94440d
--- /dev/null
+++ b/collector/lib/rust/scraper/src/bpf/tasks.bpf.c
@@ -0,0 +1,43 @@
+#include <vmlinux.h>
+
+#include <bpf/bpf_helpers.h>
+
+int count = 0;
+int tgid = 0;
+int last_tgid = 0;
+int unique_tgid_count = 0;
+
+SEC("iter/task_file")
+int iter_tasks(struct bpf_iter__task_file* ctx) {
+  struct seq_file* seq = ctx->meta->seq;
+  struct task_struct* task = ctx->task;
+  struct file* file = ctx->file;
+  __u32 fd = ctx->fd;
+
+  if (task == (void*)0 || file == (void*)0) {
+    return 0;
+  }
+
+  if (ctx->meta->seq_num == 0) {
+    count = 0;
+    BPF_SEQ_PRINTF(seq, "    tgid      gid       fd      file\n");
+  }
+
+  if (tgid == task->tgid && task->tgid != task->pid) {
+    count++;
+  }
+
+  if (last_tgid != task->tgid) {
+    last_tgid = task->tgid;
+    unique_tgid_count++;
+  }
+
+  BPF_SEQ_PRINTF(seq, "%s (%s) %8d %8d %8d %lx\n",
+                 task->mm->exe_file->f_path.dentry->d_iname,
+                 task->comm,
+                 task->tgid,
+                 task->pid,
+                 fd,
+                 (long)file->f_op);
+  return 0;
+}
diff --git a/collector/lib/rust/scraper/src/common.rs b/collector/lib/rust/scraper/src/common.rs
new file mode 100644
index 0000000000..3b2a855068
--- /dev/null
+++ b/collector/lib/rust/scraper/src/common.rs
@@ -0,0 +1,25 @@
+pub mod bpf {
+    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+    use std::ffi::CStr;
+
+    impl bpf_prog_result {
+        pub fn name(&self) -> String {
+            unsafe {
+                CStr::from_ptr(self.name.as_ptr())
+                    .to_str()
+                    .unwrap_or("")
+                    .to_string()
+            }
+        }
+
+        pub fn attached(&self) -> String {
+            unsafe {
+                CStr::from_ptr(self.attached.as_ptr())
+                    .to_str()
+                    .unwrap_or("")
+                    .to_string()
+            }
+        }
+    }
+}
diff --git a/collector/lib/rust/scraper/src/lib.rs b/collector/lib/rust/scraper/src/lib.rs
new file mode 100644
index 0000000000..0d20c16c3f
--- /dev/null
+++ b/collector/lib/rust/scraper/src/lib.rs
@@ -0,0 +1,14 @@
+pub mod bpf;
+pub mod common;
+pub mod network;
+
+#[cxx::bridge(namespace = "collector::rust")]
+mod ffi {
+    extern "Rust" {
+        fn get_bpf_programs() -> Vec<::common::bpf::bpf_prog_result>;
+    }
+}
+
+pub fn get_bpf_programs() -> Vec<common::bpf::bpf_prog_result> {
+    vec![]
+}
diff --git a/collector/lib/rust/scraper/src/main.rs b/collector/lib/rust/scraper/src/main.rs
new file mode 100644
index 0000000000..7a93b98809
--- /dev/null
+++ b/collector/lib/rust/scraper/src/main.rs
@@ -0,0 +1,90 @@
+use anyhow::Result;
+use libbpf_rs::skel::OpenSkel;
+use libbpf_rs::skel::SkelBuilder;
+use libbpf_rs::Iter;
+use std::io;
+use std::io::BufRead;
+use std::mem::MaybeUninit;
+
+use clap::{Parser, ValueEnum};
+
+mod tasks {
+    include!(concat!(env!("OUT_DIR"), "/tasks.bpf.skel.rs"));
+}
+
+mod bpf;
+mod common;
+mod network;
+
+use tasks::*;
+
+#[derive(Debug, ValueEnum, Clone)]
+enum Probe {
+    Process,
+    Network,
+    Bpf,
+}
+
+#[derive(Parser, Debug)]
+#[command(version, about, long_about=None)]
+struct Args {
+    probe: Probe,
+
+    #[arg(short, long, action=clap::ArgAction::SetTrue)]
+    debug: Option<bool>,
+}
+
+fn do_network(debug: bool) -> Result<()> {
+    let mut network = network::NetworkScraper::new(debug);
+    network.start().unwrap();
+
+    for line in network {
+        println!("{:?}", line);
+    }
+
+    Ok(())
+}
+
+fn do_bpf(debug: bool) -> Result<()> {
+    let mut bpf = bpf::BPFScraper::new(debug);
+    bpf.start().unwrap();
+
+    for line in bpf.into_iter() {
+        println!("{} ({})", line.name(), line.attached());
+    }
+
+    Ok(())
+}
+
+fn do_process(debug: bool) -> Result<()> {
+    let mut builder = TasksSkelBuilder::default();
+    builder.obj_builder.debug(debug);
+
+    let mut open_object = MaybeUninit::uninit();
+    let open = builder.open(&mut open_object)?;
+
+    let skel = open.load()?;
+
+    let link = skel.progs.iter_tasks.attach()?;
+
+    let iter = Iter::new(&link)?;
+    let reader = io::BufReader::new(iter);
+
+    for line in reader.lines().map_while(Result::ok) {
+        println!("{}", line);
+    }
+
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let args = Args::parse();
+
+    let debug = args.debug.unwrap_or(false);
+
+    match args.probe {
+        Probe::Process => do_process(debug),
+        Probe::Network => do_network(debug),
+        Probe::Bpf => do_bpf(debug),
+    }
+}
diff --git a/collector/lib/rust/scraper/src/network.rs b/collector/lib/rust/scraper/src/network.rs
new file mode 100644
index 0000000000..0d647fac24
--- /dev/null
+++ b/collector/lib/rust/scraper/src/network.rs
@@ -0,0 +1,106 @@
+use std::io::{self, BufRead};
+use std::net::{Ipv4Addr, Ipv6Addr};
+use std::{mem::MaybeUninit, net::IpAddr};
+
+use libbpf_rs::skel::OpenSkel;
+use libbpf_rs::skel::SkelBuilder;
+
+use anyhow::Result;
+use libbpf_rs::Iter;
+use network_kernel::NetworkSkelBuilder;
+
+mod network_kernel {
+    include!(concat!(env!("OUT_DIR"), "/network.bpf.skel.rs"));
+}
+
+#[derive(Debug)]
+#[allow(dead_code)]
+pub struct Connection {
+    local: IpAddr,
+    remote: IpAddr,
+    state: u8,
+}
+
+#[allow(dead_code)]
+pub struct NetworkScraper {
+    link: Option<libbpf_rs::Link>,
+    debug: bool,
+    reader: Option<io::BufReader<Iter>>,
+}
+
+impl NetworkScraper {
+    pub fn new(debug: bool) -> Self {
+        Self {
+            link: None,
+            reader: None,
+            debug,
+        }
+    }
+
+    pub fn start(&mut self) -> Result<()> {
+        let mut builder = NetworkSkelBuilder::default();
+        builder.obj_builder.debug(self.debug);
+
+        let mut open_object = MaybeUninit::uninit();
+        let open = builder.open(&mut open_object)?;
+
+        let skel = open.load()?;
+
+        self.link = Some(skel.progs.dump_tcp4.attach()?);
+
+        println!("started");
+        Ok(())
+    }
+
+    #[allow(dead_code)]
+    pub fn stop(&self) -> Result<()> {
+        Ok(())
+    }
+
+    fn parse_connection(&self, line: &str) -> Option<Connection> {
+        let parts: Vec<&str> = line.split_whitespace().collect();
+
+        let local_raw = parts[1];
+        let remote_raw = parts[2];
+        let state = parts[3];
+
+        Some(Connection {
+            local: self.parse_addr(local_raw),
+            remote: self.parse_addr(remote_raw),
+            state: u8::from_str_radix(state, 16).unwrap_or(0),
+        })
+    }
+
+    fn parse_addr(&self, raw: &str) -> IpAddr {
+        let sections: Vec<&str> = raw.split(':').collect();
+
+        if sections[0].len() == 8 {
+            let ip = u32::from_str_radix(sections[0], 16).unwrap();
+            Ipv4Addr::from_bits(ip).into()
+        } else {
+            let ip = u128::from_str_radix(sections[0], 16).unwrap();
+            Ipv6Addr::from_bits(ip).into()
+        }
+    }
+}
+
+impl Iterator for NetworkScraper {
+    type Item = Connection;
+
+    fn next(&mut self) -> Option<Connection> {
+        let Some(l) = &self.link else {
+            println!("no link");
+            return None;
+        };
+        let iter = Iter::new(l).unwrap();
+        let mut r = io::BufReader::new(iter);
+
+        let mut buf = String::new();
+        let Ok(_) = r.read_line(&mut buf) else {
+            println!("line read failed");
+            return None;
+        };
+
+        self.parse_connection(&buf)
+    }
+}
diff --git a/collector/lib/rust/test/CMakeLists.txt b/collector/lib/rust/test/CMakeLists.txt
new file mode 100644
index 0000000000..abfced0b81
--- /dev/null
+++ b/collector/lib/rust/test/CMakeLists.txt
@@ -0,0 +1,8 @@
+
+add_rust_library(TARGET rust_test
+    SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+    BINARY_DIRECTORY "${CMAKE_BINARY_DIR}"
+    INCLUDE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+)
+
+add_library(rust_test::rust_static_lib ALIAS rust_test)
diff --git a/collector/lib/rust/test/Cargo.lock b/collector/lib/rust/test/Cargo.lock
new file mode 100644
index 0000000000..9510e19e48
--- /dev/null
+++ b/collector/lib/rust/test/Cargo.lock
@@ -0,0 +1,430 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "cbindgen"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc"
+dependencies = [
+ "clap",
+ "heck",
+ "indexmap",
+ "log",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "syn 1.0.109",
+ "tempfile",
+ "toml",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags 1.3.2",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "libc"
+version = "0.2.162"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "once_cell"
+version = "1.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rust_test"
+version = "0.1.0"
+dependencies = [
+ "cbindgen",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
+dependencies = [
+ "bitflags 2.6.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "serde"
+version = "1.0.215"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.215"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.87",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.132"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/collector/lib/rust/test/Cargo.toml b/collector/lib/rust/test/Cargo.toml
new file mode 100644
index 0000000000..f21330d3e7
--- /dev/null
+++ b/collector/lib/rust/test/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "rust_test"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+
+[lib]
+crate-type = ["staticlib"]
+name = "rust_test"
+
+[build-dependencies]
+cbindgen = "0.20"
diff --git a/collector/lib/rust/test/build.rs b/collector/lib/rust/test/build.rs
new file mode 100644
index 0000000000..bdca2e7f79
--- /dev/null
+++ b/collector/lib/rust/test/build.rs
@@ -0,0 +1,21 @@
+extern crate cbindgen;
+
+use std::env;
+use std::path::Path;
+
+fn main() {
+    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+    let target_dir = env::var("BINDINGS_INCLUDE_DIRECTORY").unwrap_or("./include".to_string());
+
+    let path = Path::new(&target_dir).join("RustTest.h");
+
+    cbindgen::Builder::new()
+        .with_crate(crate_dir)
+        .with_header("// clang-format off\n// This file is autogenerated DO NOT EDIT")
+        .with_trailer("// clang-format on")
+        .with_pragma_once(true)
+        .with_namespace("collector::rust")
+        .generate()
+        .expect("Unable to generate bindings for Test library")
+        .write_to_file(path);
+}
diff --git a/collector/lib/rust/test/include/RustTest.h b/collector/lib/rust/test/include/RustTest.h
new file mode 100644
index 0000000000..9ab027d59c
--- /dev/null
+++ b/collector/lib/rust/test/include/RustTest.h
@@ -0,0 +1,22 @@
+// clang-format off
+// This file is autogenerated DO NOT EDIT
+
+#pragma once
+
+#include <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <ostream>
+#include <new>
+
+namespace collector::rust {
+
+extern "C" {
+
+uint64_t external_add(uint64_t left, uint64_t right);
+
+} // extern "C"
+
+} // namespace collector::rust
+
+// clang-format on
diff --git a/collector/lib/rust/test/src/lib.rs b/collector/lib/rust/test/src/lib.rs
new file mode 100644
index 0000000000..e178fd6aab
--- /dev/null
+++ b/collector/lib/rust/test/src/lib.rs
@@ -0,0 +1,19 @@
+#[no_mangle]
+pub extern "C" fn external_add(left: u64, right: u64) -> u64 {
+    add(left, right)
+}
+
+fn add(left: u64, right: u64) -> u64 {
+    left + right
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn it_works() {
+        let result = add(2, 2);
+        assert_eq!(result, 4);
+    }
+}
diff --git a/collector/lib/system-inspector/Service.cpp b/collector/lib/system-inspector/Service.cpp
index f0867df6d6..ca928f5f91 100644
--- a/collector/lib/system-inspector/Service.cpp
+++ b/collector/lib/system-inspector/Service.cpp
@@ -162,13 +162,14 @@ sinsp_evt* Service::GetNext() {
     return nullptr;
   }
 
-  HostInfo& host_info = HostInfo::Instance();
+  // HostInfo& host_info = HostInfo::Instance();
+  auto host_info = collector::rust::host_info();
 
   // This additional userspace filter is a guard against additional events
   // from the eBPF probe. This can occur when using sys_enter and sys_exit
   // tracepoints rather than a targeted approach, which we currently only do
   // on RHEL7 with backported eBPF
-  if (host_info.IsRHEL76() && !global_event_filter_[event->get_type()]) {
+  if (host_info->is_rhel76() && !global_event_filter_[event->get_type()]) {
     return nullptr;
   }
 
diff --git a/collector/test/HostHeuristicsTest.cpp b/collector/test/HostHeuristicsTest.cpp
index dfa737652f..85529823b3 100644
--- a/collector/test/HostHeuristicsTest.cpp
+++ b/collector/test/HostHeuristicsTest.cpp
@@ -23,7 +23,7 @@ You should have received a copy of the GNU General Public License along with thi
 
 #include "CollectorConfig.cpp"
 #include "HostConfig.h"
-#include "HostHeuristics.cpp"
+// #include "HostHeuristics.cpp"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -31,47 +31,47 @@ using namespace testing;
 
 namespace collector {
 
-class MockCPUHeuristic : public CPUHeuristic {
- public:
-  MockCPUHeuristic() = default;
-};
-
-class MockHostInfoHeuristics : public HostInfo {
- public:
-  MockHostInfoHeuristics() = default;
-
-  MOCK_METHOD0(IsUEFI, bool());
-  MOCK_METHOD0(GetSecureBootStatus, SecureBootStatus());
-  MOCK_METHOD0(GetKernelVersion, KernelVersion());
-  MOCK_METHOD0(IsGarden, bool());
-  MOCK_METHOD0(GetDistro, std::string&());
-  MOCK_METHOD0(NumPossibleCPU, int());
-};
-
-class MockCollectorConfig : public CollectorConfig {
- public:
-  MockCollectorConfig()
-      : CollectorConfig() {};
-
-  void SetCollectionMethod(CollectionMethod cm) {
-    if (host_config_.HasCollectionMethod()) {
-      host_config_.SetCollectionMethod(cm);
-    }
-    collection_method_ = cm;
-  }
-};
-
-TEST(HostHeuristicsTest, TestCPUHeuristic) {
-  MockCPUHeuristic cpuHeuristic;
-  MockHostInfoHeuristics host;
-  MockCollectorConfig config;
-  HostConfig hconfig;
-
-  EXPECT_CALL(host, NumPossibleCPU()).WillOnce(Return(10));
-
-  cpuHeuristic.Process(host, config, &hconfig);
-
-  EXPECT_EQ(hconfig.GetNumPossibleCPUs(), 10);
-}
+// class MockCPUHeuristic : public CPUHeuristic {
+//  public:
+//   MockCPUHeuristic() = default;
+// };
+//
+// class MockHostInfoHeuristics : public HostInfo {
+//  public:
+//   MockHostInfoHeuristics() = default;
+//
+//   MOCK_METHOD0(IsUEFI, bool());
+//   MOCK_METHOD0(GetSecureBootStatus, SecureBootStatus());
+//   MOCK_METHOD0(GetKernelVersion, KernelVersion());
+//   MOCK_METHOD0(IsGarden, bool());
+//   MOCK_METHOD0(GetDistro, std::string&());
+//   MOCK_METHOD0(NumPossibleCPU, int());
+// };
+//
+// class MockCollectorConfig : public CollectorConfig {
+//  public:
+//   MockCollectorConfig()
+//       : CollectorConfig() {};
+//
+//   void SetCollectionMethod(CollectionMethod cm) {
+//     if (host_config_.HasCollectionMethod()) {
+//       host_config_.SetCollectionMethod(cm);
+//     }
+//     collection_method_ = cm;
+//   }
+// };
+//
+// TEST(HostHeuristicsTest, TestCPUHeuristic) {
+//   MockCPUHeuristic cpuHeuristic;
+//   MockHostInfoHeuristics host;
+//   MockCollectorConfig config;
+//   HostConfig hconfig;
+//
+//   EXPECT_CALL(host, NumPossibleCPU()).WillOnce(Return(10));
+//
+//   cpuHeuristic.Process(host, config, &hconfig);
+//
+//   EXPECT_EQ(hconfig.GetNumPossibleCPUs(), 10);
+// }
 
 }  // namespace collector