rama-core

The async Service and Layer trait core that every Rama network stack builds on.

Library
Cargo
v0.4.0
1,178stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
87/100Excellent
Development Activity100
Maintenance96
Community56
Maturity56
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
82/100Excellent
Architecture90
Code Quality88
Innovation78
Learning Curve70

rama-core is the foundational crate of the Rama framework: it defines the Service and BlockingService traits, the Layer middleware abstraction, Extensions-based dynamic state, matcher combinators, and graceful-shutdown/runtime helpers that every other rama-* crate (HTTP, TLS, SOCKS5, DNS, proxy, and more) is built on top of.

It is heavily inspired by tower-service but reworked to fit Rama’s needs: async-fn-in-trait serve methods instead of poll_ready/call, first-class BoxService dynamic dispatch, no_std-compatible core types behind a std feature flag, and an Extensions type for passing optional state between services without forcing every caller to thread it through generics.

Most application authors depend on the umbrella rama crate rather than rama-core directly, but anyone building a custom Rama-compatible service, layer, or protocol crate depends on rama-core as the contract every other piece of the ecosystem agrees on.

What You Get

  • Service and BlockingService traits - async-fn-in-trait serve(&self, input) -> Result<Output, Error> contracts, with blanket impls for Arc<S>, Box<S>, and &'static S
  • BoxService - type-erased dynamic dispatch for services, built on the async-trait-object pattern rather than a dyn Future wrapper crate
  • Layer middleware trait - the tower-style layer(inner) -> Service abstraction, plus an Option<Layer> blanket impl for conditionally-enabled middleware
  • Extensions - a typed, TypeId-keyed bag for passing optional per-request/per-connection state between services without generic parameter threading
  • matcher combinators - and/or/not boolean composition over request/connection matchers used for routing and conditional middleware
  • graceful shutdown and rt helpers - runtime-agnostic utilities for coordinating shutdown across a stack of long-lived services
  • no_std support - the crate’s core types compile without std when the std feature is disabled, for constrained targets
  • built-in service types - MirrorService, RejectService, and StaticOutput for tests, stubs, and simple fallback behavior

Common Use Cases

  • Writing a custom Rama layer - implement Layer<S> to add logging, rate limiting, or auth in front of an existing service without modifying it
  • Building a new protocol crate for the Rama ecosystem - implement Service<Input> against rama-core’s traits so it composes with the rest of rama-*
  • Dynamic middleware stacks - use BoxService to hold a Vec of heterogeneous services behind one type, e.g. per-route or per-tenant handler chains
  • Passing optional per-connection state - use Extensions to attach things like a parsed TLS client hello or peer identity without changing every downstream service’s signature
  • Conditional middleware from config - wrap a Layer in Option so a stack can enable/disable a middleware layer based on a runtime flag with zero boilerplate

Under The Hood

Architecture rama-core is organized as a small set of focused modules under src/: service (the Service/BlockingService/BoxService traits in svc.rs), layer (the Layer trait and ~15 built-in layers like timeout, limit, map_err, hijack in individual files under layer/), matcher (boolean combinators over matchers plus a service submodule for matcher-as-service adapters), extensions (the TypeId-keyed state bag), graceful/rt (shutdown coordination, std-gated), and telemetry (tracing and optional OpenTelemetry integration). lib.rs is a thin re-export surface with #![no_std] support gated behind a std feature, so most modules are conditionally compiled with #[cfg(feature = "std")]. The Service trait is deliberately minimal — one serve method plus a boxed() convenience — with blanket implementations for Arc<S>, Box<S>, &'static S, and () (an identity/pass-through service), which is what lets arbitrarily deep layer stacks stay composable without extra wrapper types.

Tech Stack The crate targets Rust edition 2024 with rust-version = "1.96.0"" and relies on async-fn-in-trait rather than a boxed-future crate for its core Service::servesignature. Dependencies are kept intentionally small and mostly optional behind thestdfeature:bytesfor itsByteStrextension type,pin-project-litefor pin-safe combinators,tokio(feature-gated tomacros/fs/io-std/rt/sync/time) for the std-only runtime helpers, tokio-gracefulfor shutdown coordination,parking_lotfor locking,tracing(required,no_std-compatible subset) with optional tracing-opentelemetry/opentelemetryfor a fully-gatedopentelemetryfeature, and sibling workspace cratesrama-error, rama-macros, and rama-utils. An experimental dial9feature routes task spawns through an internal telemetry crate for wake-event tracking undertokio_unstable`.

Code Quality Every core module carries an inline #[cfg(test)] mod tests block using #[tokio::test], and svc.rs alone has explicit assert_send/assert_sync trait-bound tests plus behavioral tests for static and dynamic dispatch, Arc-wrapped services, and the built-in RejectService. The workspace enforces lints via a shared [lints] workspace = true table plus a clippy.toml and deny.toml (cargo-deny for dependency/license auditing), and CI runs multiple GitHub Actions workflows including a dedicated CI-unstable.yml for nightly/tokio_unstable coverage. Error types use the rama_utils::macros::error::static_str_error! macro for zero-alloc static error types rather than ad-hoc String errors, and public types consistently derive Debug/Clone with hand-written Debug impls where derive can’t express the intent (e.g. RejectService’s PhantomData field).

What Makes It Unique Where tower-service uses a poll_ready/call split designed around explicit backpressure, rama-core collapses this into a single async-fn-in-trait serve method, trading tower’s fine-grained readiness signaling for a simpler mental model better suited to Rama’s proxy/network-stack use case. It pairs this with a genuinely no_std-capable core (most of tower’s ecosystem assumes std), an Option<Layer> blanket impl that turns “is this middleware enabled” into an ordinary optional-value question instead of a runtime branch inside every layer, and an Extensions state-passing mechanism designed explicitly around graceful absence — the module docs are explicit that consumers should handle a missing extension rather than unwrap/panic, which reflects the crate’s origin serving MITM/proxy workloads where upstream data is often incomplete or adversarial.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search