# How to Implement a Custom Platform Provider for LiteBox: A Complete Guide

> Learn to implement a custom Platform provider for LiteBox by creating a struct that implements Provider and its sub-traits. Inject it into ShimBuilder for your guest.

- Repository: [Microsoft/litebox](https://github.com/microsoft/litebox)
- Tags: how-to-guide
- Published: 2026-02-16

---

**To implement a custom Platform provider for LiteBox, you must create a zero-sized struct that implements the `Provider` umbrella trait and its seven mandatory sub-traits (`RawMutexProvider`, `IPInterfaceProvider`, `TimeProvider`, `PunchthroughProvider`, `DebugLogProvider`, and `RawPointerProvider`), then inject it into the `ShimBuilder` before running the guest.**

LiteBox isolates guest programs from the host by delegating every OS-level service—mutexes, time, networking, and syscalls—to a pluggable **platform provider**. By writing a custom Platform provider for LiteBox, you can port the sandbox to a new operating system, embed it inside a specialized kernel, or run it under a hypervisor. This guide walks through the architecture, required traits, and a minimal working implementation based on the official Microsoft repository.

## Architecture of a LiteBox Platform Provider

### The Provider Umbrella Trait

At the center of the system is the empty `Provider` trait defined in [`litebox/src/platform/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/mod.rs). It serves as a capability aggregator, requiring implementors to satisfy several smaller sub-traits:

```rust
pub trait Provider:
    RawMutexProvider
    + IPInterfaceProvider
    + TimeProvider
    + PunchthroughProvider
    + DebugLogProvider
    + RawPointerProvider
{}

```

*Source:* [`litebox/src/platform/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/mod.rs) (lines 38–50)

A concrete platform type never implements methods on `Provider` itself; instead, it implements the sub-traits listed above.

### Required Sub-traits for a Custom Platform Provider

To create a functional custom Platform provider for LiteBox, you must implement the following seven sub-traits. Each maps to a specific host capability:

| Sub-trait | Purpose | Implementation Notes |
|-----------|---------|-------------------|
| `RawMutexProvider` | Futex-style locking primitive | Wrap an `AtomicU32` and call the host’s futex or `WaitOnAddress` API. Reference: [`litebox_platform_linux_kernel/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_platform_linux_kernel/src/lib.rs) (lines 20–31). |
| `IPInterfaceProvider` | Raw IPv4 packet I/O | Implement `send_ip_packet` and `receive_ip_packet` using host TUN devices or raw sockets. |
| `TimeProvider` | Monotonic `Instant` and wall-clock `SystemTime` | Use `rdtsc` on x86, `QueryUnbiasedInterruptTimePrecise` on Windows, or `clock_gettime` on POSIX. |
| `PunchthroughProvider` | Escape-hatch for non-standard host calls (e.g., FS-base manipulation) | Use `ImpossiblePunchthroughProvider` from [`litebox/src/platform/trivial_providers.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/trivial_providers.rs) if no extra syscalls are needed. |
| `DebugLogProvider` | Debug output | Write to `stderr` on POSIX or `OutputDebugString` on Windows. |
| `RawPointerProvider` | Type-safe raw pointer wrappers | Re-use `UserConstPtr` and `UserMutPtr` from `litebox::platform::common_providers::userspace_pointers` for user-space hosts. |

Optional traits such as `ThreadProvider` and `PageManagementProvider` are required only if your guest creates threads or manages its own memory mappings. See [`litebox_platform_linux_kernel/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_platform_linux_kernel/src/lib.rs) and [`litebox_platform_windows_userland/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_platform_windows_userland/src/lib.rs) for production implementations of these advanced traits.

## Step-by-Step: Building a Custom Platform Provider

The following minimal example creates a provider named `MyHost` suitable for a POSIX-like environment. It re-uses LiteBox’s trivial implementations for raw pointers and punch-throughs while providing stubbed (but compilable) implementations for networking and time.

### Step 1: Define the Host Structure

Create a zero-sized struct. The provider should hold no runtime state unless the underlying host requires it.

```rust
// src/my_host.rs
use litebox::platform::{
    Provider, RawMutexProvider, IPInterfaceProvider, TimeProvider,
    PunchthroughProvider, DebugLogProvider, RawPointerProvider,
};

pub struct MyHost;

impl Provider for MyHost {}

```

### Step 2: Implement RawMutexProvider

Wrap an `AtomicU32` to serve as the futex word. The `wake_many` method should invoke the host’s futex wake primitive (stubbed here).

```rust
use core::sync::atomic::AtomicU32;
use litebox::platform::{RawMutex, RawMutexProvider};

pub struct MyRawMutex {
    inner: AtomicU32,
}

impl MyRawMutex {
    pub const fn new() -> Self {
        Self { inner: AtomicU32::new(0) }
    }
}

unsafe impl Send for MyRawMutex {}
unsafe impl Sync for MyRawMutex {}

impl RawMutex for MyRawMutex {
    const INIT: Self = Self::new();

    fn underlying_atomic(&self) -> &AtomicU32 {
        &self.inner
    }

    fn wake_many(&self, _n: usize) -> usize {
        // TODO: Replace with host futex wake or WaitOnAddress
        0
    }
}

impl RawMutexProvider for MyHost {
    type RawMutex = MyRawMutex;
}

```

### Step 3: Implement TimeProvider

Define `Instant` and `SystemTime` types, then implement the trait using the host’s monotonic and real-time clocks.

```rust
use core::time::Duration;
use litebox::platform::{Instant as InstantTrait, SystemTime as SystemTimeTrait, TimeProvider};

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MyInstant(u64); // nanoseconds

impl InstantTrait for MyInstant {
    fn checked_duration_since(&self, earlier: &Self) -> Option<Duration> {
        self.0.checked_sub(earlier.0).map(|ns| Duration::from_nanos(ns))
    }
    fn checked_add(&self, dur: Duration) -> Option<Self> {
        self.0.checked_add(dur.as_nanos() as u64).map(MyInstant)
    }
}

pub struct MySystemTime {
    filetime: u64, // 100-ns intervals
}

impl SystemTimeTrait for MySystemTime {
    const UNIX_EPOCH: Self = MySystemTime { filetime: 0 };
    
    fn duration_since(&self, earlier: &Self) -> Result<Duration, Duration> {
        if self.filetime >= earlier.filetime {
            Ok(Duration::from_nanos((self.filetime - earlier.filetime) * 100))
        } else {
            Err(Duration::from_nanos((earlier.filetime - self.filetime) * 100))
        }
    }
}

impl TimeProvider for MyHost {
    type Instant = MyInstant;
    type SystemTime = MySystemTime;
    
    fn now(&self) -> Self::Instant {
        // Example using libc::clock_gettime
        unsafe {
            let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
            libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
            MyInstant(ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64)
        }
    }
    
    fn current_time(&self) -> Self::SystemTime {
        unsafe {
            let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
            libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts);
            MySystemTime { 
                filetime: (ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64) / 100 
            }
        }
    }
}

```

### Step 4: Implement IPInterfaceProvider

Stub out packet I/O; replace with TUN device logic or raw sockets for real networking.

```rust
use litebox::platform::{IPInterfaceProvider, SendError, ReceiveError};

impl IPInterfaceProvider for MyHost {
    fn send_ip_packet(&self, _packet: &[u8]) -> Result<(), SendError> {
        // TODO: Write to host TUN device
        Ok(())
    }
    
    fn receive_ip_packet(&self, _packet: &mut [u8]) -> Result<usize, ReceiveError> {
        // TODO: Read from host TUN device
        Err(ReceiveError::WouldBlock)
    }
}

```

### Step 5: Implement PunchthroughProvider

Use the `ImpossiblePunchthroughProvider` from [`trivial_providers.rs`](https://github.com/microsoft/litebox/blob/main/trivial_providers.rs) if you do not need special host escapes like FS-base manipulation.

```rust
use litebox::platform::trivial_providers::ImpossiblePunchthroughProvider;
use litebox::platform::{PunchthroughProvider, PunchthroughToken};

impl PunchthroughProvider for MyHost {
    type PunchthroughToken<'a> = <ImpossiblePunchthroughProvider as PunchthroughProvider>::PunchthroughToken<'a>;
    
    fn get_punchthrough_token_for<'a>(
        &self,
        _: <Self::PunchthroughToken<'a> as PunchthroughToken>::Punchthrough,
    ) -> Option<Self::PunchthroughToken<'a>> {
        None
    }
}

```

### Step 6: Implement DebugLogProvider and RawPointerProvider

Forward debug logs to `stderr` and reuse LiteBox’s transparent user-space pointers.

```rust
use std::io::Write;
use litebox::platform::{DebugLogProvider, RawPointerProvider};
use litebox::platform::common_providers::userspace_pointers::{UserConstPtr, UserMutPtr};
use zerocopy::{FromBytes, IntoBytes};

impl DebugLogProvider for MyHost {
    fn debug_log_print(&self, msg: &str) {
        let _ = std::io::stderr().write_all(msg.as_bytes());
    }
}

impl RawPointerProvider for MyHost {
    type RawConstPointer<T: FromBytes> = UserConstPtr<T>;
    type RawMutPointer<T: FromBytes + IntoBytes> = UserMutPtr<T>;
}

```

## Wiring Your Custom Provider into a LiteBox Guest

Once your provider is implemented, inject it into the shim using `ShimBuilder` before entering the guest. The following pattern mirrors the initialization code found in `litebox_platform_linux_kernel::run_thread` and `litebox_platform_windows_userland::run_thread`.

```rust
use litebox::shim::{EnterShim, ShimBuilder};
use litebox_common_linux::PtRegs;

// 1. Instantiate the provider (typically 'static for the process lifetime)
static HOST: MyHost = MyHost;

// 2. Build the shim with your custom platform
let shim = ShimBuilder::new()
    .with_platform(&HOST)   // Inject the custom Platform provider
    .build()?;              // Returns an EnterShim instance

// 3. Prepare initial guest context (e.g., from ELF loader)
let mut ctx = PtRegs::default();
// ... populate registers ...

// 4. Enter the guest
unsafe { shim.enter(&mut ctx) };

```

## Key Source Files to Reference

When building a custom Platform provider for LiteBox, study these reference implementations in the `microsoft/litebox` repository:

- **[`litebox/src/platform/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/mod.rs)** – Defines the `Provider` umbrella trait and all sub-traits including `RawMutexProvider`, `TimeProvider`, and `IPInterfaceProvider`.
- **[`litebox/src/platform/trivial_providers.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/trivial_providers.rs)** – Supplies reusable components like `ImpossiblePunchthroughProvider`, `TransparentConstPtr`, and `TransparentMutPtr` for user-space hosts.
- **[`litebox_platform_linux_kernel/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_platform_linux_kernel/src/lib.rs)** – Production kernel-mode provider demonstrating futex-based `RawMutex`, thread creation via `clone`, and FS-base punch-through handling.
- **[`litebox_platform_windows_userland/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_platform_windows_userland/src/lib.rs)** – Windows user-land provider showing `WaitOnAddress` mutexes, `NtContinue`-based guest switching, and TLS management.
- **[`litebox_common_linux/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_common_linux/src/lib.rs)** – Defines the `PunchthroughSyscall` enum used when implementing `PunchthroughProvider` for Linux hosts.

## Production Checklist for Custom Platform Providers

Before deploying a custom Platform provider for LiteBox, verify the following:

- **Implement all mandatory sub-traits** – `RawMutexProvider`, `IPInterfaceProvider`, `TimeProvider`, `PunchthroughProvider`, `DebugLogProvider`, and `RawPointerProvider` are required for every provider.
- **Add thread support** – Implement `ThreadProvider` if your guest creates threads; reference the Linux kernel provider’s `clone` wrapper or the Windows provider’s `CreateThread` usage.
- **Handle memory management** – Implement `PageManagementProvider` using host syscalls like `mmap` or `VirtualAlloc` if the guest manages its own address space.
- **Configure punch-throughs** – Use `ImpossiblePunchthroughProvider` for pure sandboxes, or implement real FS-base/thread-local storage punch-throughs by handling `PunchthroughSyscall` variants.
- **Validate with integration tests** – Run the `dev_tests` and `litebox_syscall_rewriter` test suites against your provider to ensure syscall compatibility.
- **Document host requirements** – Add a [`README.md`](https://github.com/microsoft/litebox/blob/main/README.md) explaining any host-specific setup, such as TUN device permissions for `IPInterfaceProvider` or required kernel capabilities.

## Summary

Implementing a custom Platform provider for LiteBox involves creating a zero-sized struct that implements seven core sub-traits defined in [`litebox/src/platform/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/mod.rs). The provider acts as a bridge between the guest sandbox and the host OS, handling synchronization primitives, time, networking, and debug logging. For rapid prototyping, reuse the `ImpossiblePunchthroughProvider` and transparent pointer types from [`litebox/src/platform/trivial_providers.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/trivial_providers.rs). For production deployments, study the Linux kernel and Windows user-land reference implementations to handle threads, memory management, and host-specific punch-throughs like FS-base manipulation.

## Frequently Asked Questions

### What is the minimum code required to create a custom Platform provider for LiteBox?

The absolute minimum requires a struct implementing the `Provider` trait (which is empty) and the seven mandatory sub-traits. You can satisfy `PunchthroughProvider` with `ImpossiblePunchthroughProvider` and `RawPointerProvider` with `UserConstPtr`/`UserMutPtr` from `litebox::platform::common_providers::userspace_pointers`. At a minimum, you must provide working implementations for `RawMutexProvider` (using an `AtomicU32`), `TimeProvider` (using host clocks), and `DebugLogProvider` (writing to stderr).

### How do I handle thread creation in a custom Platform provider?

If your guest binary spawns threads, your custom Platform provider must implement the `ThreadProvider` trait. Refer to [`litebox_platform_linux_kernel/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_platform_linux_kernel/src/lib.rs) for a kernel-mode example that wraps the `clone` syscall, or [`litebox_platform_windows_userland/src/lib.rs`](https://github.com/microsoft/litebox/blob/main/litebox_platform_windows_userland/src/lib.rs) for a user-land example using `CreateThread` and `NtContinue` for context switching. You must also ensure your `RawMutexProvider` uses a host primitive that supports cross-thread synchronization, such as Linux futexes or Windows `WaitOnAddress`.

### Can I implement a custom Platform provider for LiteBox without networking support?

Yes. If your sandboxed guest does not require network access, you can provide a stub implementation of `IPInterfaceProvider` that returns `Err(ReceiveError::WouldBlock)` from `receive_ip_packet` and silently drops packets in `send_ip_packet`. This satisfies the trait contract while disabling networking. However, if your guest expects a functional TCP/IP stack, you must implement real packet forwarding to a host TUN device or raw socket as shown in the Linux kernel provider.

### Where does LiteBox call the Platform provider during guest execution?

LiteBox invokes the Platform provider through the shim layer. When you build the shim using `ShimBuilder::with_platform`, the provider is stored inside the shim context. During guest execution, whenever the guest triggers a syscall or needs a platform service (e.g., locking a mutex, checking the time, sending a network packet), LiteBox dispatches to the corresponding method on your provider. For example, FS-base manipulation on Linux flows through `PunchthroughProvider::get_punchthrough_token_for` with the `GetFsBase` variant defined in `litebox_common_linux::PunchthroughSyscall`.