A batteries-included foundation for Rust services

Cloudflare has open-sourced Foundations, a Rust library that packages the operational plumbing of production services into a single, ergonomic toolkit. Originally extracted from the company's Oxy proxy framework, the library targets the gap between a working prototype and a service that can run reliably at scale across thousands of instances.

The motivation is straightforward: many concerns that are trivial on a developer laptop become genuinely hard in production. Observability tooling must work across distributed processes rather than a single shell. Configuration needs to be dynamic and structured rather than hardcoded. Security hardening, often an afterthought in local development, becomes a requirement when a service is exposed to the open internet.

Foundations is designed around three principles. First, it is highly modular: teams with existing services can adopt individual components incrementally rather than committing to a full rewrite. Second, API ergonomics are a priority, with Rust procedural macros providing an intuitive interface and reducing boilerplate. Third, the library aims for simplified setup: it is designed to work out of the box with sensible defaults, with compile-time features used to manage different workflows rather than a complex setup API.

Telemetry in one package

The library's telemetry surface covers the three pillars of observability in a unified API: logging for arbitrary tagged text, tracing for detailed timing breakdowns, and metrics for quantitative health monitoring. This consolidation means engineers interact with a single, consistent interface for all their observability needs.

Tracing with production-oriented features

The tracing API is conceptually similar to tokio/tracing, using implicit context propagation, instrumentation macros, and futures wrapping.

Several features distinguish it, however:

  • Selective sampling overrides: the global trace sampling ratio can be overridden in specific code branches, which is useful for investigating performance bugs affecting particular accounts, connections, or requests without flooding the tracing pipeline.
  • Distributed trace stitching: trace data from multiple services can be integrated into a single view, with fine-grained control allowing upstream services to dictate sampling rates for downstream traffic flows.
  • Trace forking: for long-lived connections handling many multiplexed requests, each request can get its own trace linked to the parent connection trace, simplifying analysis and improving performance.

Telemetry is treated as a first-class component rather than an optional extra, so Foundations includes APIs and macros for collecting and asserting on tracing data within tests.

Logging without logger plumbing

Logging in Foundations builds on the same foundations as tokio/tracing and slog, but addresses the cumbersome pattern of passing logger objects through request-scoped code. In a typical connection/request hierarchy, each request should carry its own contextual tags—such as the request URL—while retaining connection-level information like the connection ID and protocol version.

Rather than requiring developers to create and propagate a new logger for each request scope, the library uses future instrumentation to make the current logger implicitly available throughout the call stack, including across asynchronous boundaries. Logs can be "forked" per request and used seamlessly within the current code scope.

All context management APIs are merged into a single TelemetryContext object that is implicitly available in every code scope. This simplification also lays groundwork for future features that could weave tracing and logging information together by cross-referencing each other.

 let conn_tele_ctx = TelemetryContext::current();

 let on_request = service_fn({
        let endpoint_name = Arc::clone(&endpoint_name);

        move |req| {
            let routes = Arc::clone(&routes);
            let endpoint_name = Arc::clone(&endpoint_name);

            // Each request gets independent log inherited from the connection log and separate
            // trace linked to the connection trace.
            conn_tele_ctx
                .with_forked_log()
                .with_forked_trace("request")
                .apply(async move { respond(endpoint_name, req, routes).await })
        }
});

Metrics with less boilerplate

Metrics functionality wraps the official Prometheus Rust client library. A procedural macro simplifies metric definitions with typed labels, reducing the boilerplate typically associated with Prometheus metric declarations. Collection and structuring APIs are likewise streamlined for simplicity.

For long-lived services, the library also includes support for enabling jemalloc as the memory allocator, with its memory profiling capability exposed through a safe Rust API.

A built-in telemetry server

Foundations ships with a customizable telemetry server endpoint that handles health checks, metric collection, and memory profiling requests automatically—removing the need for services to implement and wire up these endpoints themselves.

Security via seccomp

On the security front, Foundations offers an ergonomic API for seccomp, the Linux kernel's syscall sandboxing mechanism. The API allows applications to define lists of permitted syscalls, with support for composing multiple lists together and a set of predefined lists for common use cases. Seccomp filters act as a defensive layer against threats such as arbitrary code execution by blocking unwanted syscalls at the kernel level.

  use foundations::security::common_syscall_allow_lists::{ASYNC, NET_SOCKET_API, SERVICE_BASICS};
    use foundations::security::{allow_list, enable_syscall_sandboxing, ViolationAction};

    allow_list! {
        static ALLOWED = [
            ..SERVICE_BASICS,
            ..ASYNC,
            ..NET_SOCKET_API
        ]
    }

    enable_syscall_sandboxing(ViolationAction::KillProcess, &ALLOWED)
 

With the library now public on GitHub, the example HTTP server in the repository demonstrates how these components fit together in practice. The full API is documented on docs.rs.

Configuration Comes From Documentation

Foundations treats service configuration as a first-class concern, built around a “working by default” principle. Rather than requiring operators to reverse-engineer a bespoke config schema, services define their settings as plain Rust structs and enums, with defaults expressed directly in code. Foundations maps those defaults onto CLI behavior, so a service can generate a ready-to-use, fully annotated YAML file on demand. The annotations are pulled straight from the Rustdoc comments on each setting, giving users immediate context without needing to open the source.

This means the generated default configuration isn't just a stub of keys and values—it carries the rationale for each field. If operators see a setting they want to tune, they have the explanation right there in the YAML, not in a separate manual or wiki page. For example, the http_server example in the repo shows how a few lines of Rust define a working service configuration that matches the CLI’s help text and the generated YAML output.

use foundations::settings::collections::Map;
use foundations::settings::net::SocketAddr;
use foundations::settings::settings;
use foundations::telemetry::settings::TelemetrySettings;

#[settings]
pub(crate) struct HttpServerSettings {
    /// Telemetry settings.
    pub(crate) telemetry: TelemetrySettings,
    /// HTTP endpoints configuration.
    #[serde(default = "HttpServerSettings::default_endpoints")]
    pub(crate) endpoints: Map<String, EndpointSettings>,
}

impl HttpServerSettings {
    fn default_endpoints() -> Map<String, EndpointSettings> {
        let mut endpoint = EndpointSettings::default();

        endpoint.routes.insert(
            "/hello".into(),
            ResponseSettings {
                status_code: 200,
                response: "World".into(),
            },
        );

        endpoint.routes.insert(
            "/foo".into(),
            ResponseSettings {
                status_code: 403,
                response: "bar".into(),
            },
        );

        [("Example endpoint".into(), endpoint)]
            .into_iter()
            .collect()
    }
}

#[settings]
pub(crate) struct EndpointSettings {
    /// Address of the endpoint.
    pub(crate) addr: SocketAddr,
    /// Endoint's URL path routes.
    pub(crate) routes: Map<String, ResponseSettings>,
}

#[settings]
pub(crate) struct ResponseSettings {
    /// Status code of the route's response.
    pub(crate) status_code: u16,
    /// Content of the route's response.
    pub(crate) response: String,
}

The above struct definition automatically yields the corresponding YAML template below, complete with descriptions and placeholder values:

---
# Telemetry settings.
telemetry:
  # Distributed tracing settings
  tracing:
    # Enables tracing.
    enabled: true
    # The address of the Jaeger Thrift (UDP) agent.
    jaeger_tracing_server_addr: "127.0.0.1:6831"
    # Overrides the bind address for the reporter API.
    # By default, the reporter API is only exposed on the loopback
    # interface. This won't work in environments where the
    # Jaeger agent is on another host (for example, Docker).
    # Must have the same address family as `jaeger_tracing_server_addr`.
    jaeger_reporter_bind_addr: ~
    # Sampling ratio.
    #
    # This can be any fractional value between `0.0` and `1.0`.
    # Where `1.0` means "sample everything", and `0.0` means "don't sample anything".
    sampling_ratio: 1.0
  # Logging settings.
  logging:
    # Specifies log output.
    output: terminal
    # The format to use for log messages.
    format: text
    # Set the logging verbosity level.
    verbosity: INFO
    # A list of field keys to redact when emitting logs.
    #
    # This might be useful to hide certain fields in production logs as they may
    # contain sensitive information, but allow them in testing environment.
    redact_keys: []
  # Metrics settings.
  metrics:
    # How the metrics service identifier defined in `ServiceInfo` is used
    # for this service.
    service_name_format: metric_prefix
    # Whether to report optional metrics in the telemetry server.
    report_optional: false
  # Server settings.
  server:
    # Enables telemetry server
    enabled: true
    # Telemetry server address.
    addr: "127.0.0.1:0"
# HTTP endpoints configuration.
endpoints:
  Example endpoint:
    # Address of the endpoint.
    addr: "127.0.0.1:0"
    # Endoint's URL path routes.
    routes:
      /hello:
        # Status code of the route's response.
        status_code: 200
        # Content of the route's response.
        response: World
      /foo:
        # Status code of the route's response.
        status_code: 403
        # Content of the route's response.
        response: bar

For deeper examples, see the example web server source and the API docs for settings and the CLI module.

Why Open Source

Foundations has already cut real friction from Cloudflare’s internal service development. By releasing it, the hope is that the broader community finds the same leverage in their Rust services. Cloudflare also sees the project as a two-way exchange: external use brings external experience, which can pull new ideas and priorities back into the core.

The team invites contributions to the project. If building service foundations sounds like a meaningful problem to work on, Cloudflare notes that it is also hiring.