Ferrum Family · Rust service toolkit

Ferrum Alloy

A toolkit for Axum services. Connected to the Edge.

A batteries-included toolkit for Rust API services built on Axum. Alloy handles the repetitive production setup (configuration, errors, health, limits, graceful shutdown and request telemetry) while your handlers, extractors, state, routers and Tower middleware stay ordinary Axum.

Pre-release Rust 1.94+ · edition 2024 Built on Axum PolyForm Noncommercial
Status
Pre-releaseNot on crates.io; depend on a pinned git revision
Ships as
Rust library + CLIFive crates and the ferrum-alloy command
Built with
Axum, Tokio, TowerOptional rustls, SQLx, utoipa, OpenTelemetry
Ferrum Edge
Tested with v0.9.10 and v0.9.9Current source pairing; Alloy has no published release
Pre-release. Nothing is published to crates.io yet and APIs and contracts may change. The implementation status lists exactly what is implemented and tested.
Two ways in

Start a service, or instrument the one you have

A new service is one builder call around an ordinary Axum router. An existing Axum app adds only the ferrum-alloy-telemetry Tower layer and keeps its own runtime, subscriber, router and server.

  • New service: run ferrum-alloy new orders-api, or use AlloyApp directly
  • Existing Axum app: add the telemetry layer, change nothing else
  • Escape hatch: into_parts() hands back the composed router to serve however you like
use axum::{Router, routing::get};
use ferrum_alloy::AlloyApp;

async fn hello() -> &'static str {
    "Hello from Ferrum Alloy"
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let router = Router::new().route("/hello", get(hello));

    AlloyApp::new("hello-api").router(router).run().await?;

    Ok(())
}
The complete examples/minimal service. Health, limits, Problem Details, telemetry and draining come with it.

The Production Setup, Done Once

Everything a Rust API service needs before its first real request, without replacing the Axum you already know.

Typed configuration

Strict and typed. Precedence is builder, then FERRUM_ALLOY_*, then TOML, then defaults. Secrets are redacted and errors never quote configuration values.

RFC 9457 errors

Problem Details for framework errors and for Problem-returning Json, Path, Query and ValidJson extractors. Application bodies are never rewritten.

Health and draining

Minimal liveness, cached single-flight readiness and a draining state. Detailed health sits on a token-protected management listener.

Limits and lifecycle

Body, header, connection and admission limits. A time-to-headers deadline that never cuts SSE streams. SIGTERM draining with a budget and forced close.

Truthful telemetry

Request ids and route-template metrics. W3C trace context is accepted only from trusted transport peers, and accounting ends exactly once, when the response body ends.

Optional batteries

Cargo features for OTLP export, rustls with verified client identity, Ferrum Edge trust, PostgreSQL, OpenAPI with a protected Swagger UI, JWT/JWKS, an HTTP client, compression and CORS.

What the Minimal Service Already Does

With no configuration at all, the five-line service above provides these surfaces.

SurfaceDefault
Application listener127.0.0.1:8080. Bind 0.0.0.0:8080 explicitly in containers with FERRUM_ALLOY_BIND.
Management listener127.0.0.1:9090 with /livez, /readyz, /health and /metrics
ErrorsRFC 9457 Problem Details for unmatched routes, 405, oversized bodies, timeouts, overload and panics
Limits2 MiB bodies, 100 headers / 64 KiB request head, 10 s head read, 30 s to response headers, 10 000 connections
TelemetryJSON logs with request and trace ids, one access event per request, Prometheus metrics
ShutdownOn SIGTERM/SIGINT readiness reports draining, accepting stops, in-flight requests and streams get 30 s, then telemetry flushes
Off until you opt inCORS, compression, public OpenAPI, the OpenAPI UI, OTLP export, TLS and gateway trust

One Request Story, Gateway to Service

Alloy works on its own. Behind Ferrum Edge it links each service span to the gateway span it belongs to, and records whether the gateway's identity was cryptographically verified.

ClientAny HTTP client
Ferrum EdgeRoutes, authenticates and propagates trace context
Your Alloy serviceTrusts trace context only from the verified gateway
OpenTelemetry CollectorOne trace, gateway and service spans together

Three trust modes

standalone, gateway_preferred and gateway_required. In required mode a direct request that bypasses the gateway gets 403 gateway-required.

Consumer identity handoff

Handlers can take Option<GatewayContext> for Edge's authenticated consumer, accepted only from a verified gateway identity.

Tested against a real gateway

CI runs Edge v0.9.10 and v0.9.9, Alloy and a Collector together: the documented end-to-end checks covering retries, connection reuse, concurrent HTTP/2 streams and cancellation.

ferrum-alloy diagnose

Where did the time go? And what does the evidence not prove?

Diagnosis is deterministic. It explains only the supplied evidence, from OTLP exports, report files or one request's live report fetched from a running service, and it never treats input as authenticated.

  • Every finding carries a confidence, a scope and its evidence
  • Alternative explanations and what the finding does not prove
  • Missing evidence and the next checks to run
  • Live retrieval is tenant-scoped; every refusal is the same 404
Ferrum Alloy diagnosis
  report schema: ferrum.diagnostic_report 1.0
  trace: 5b8efff798038103d269b633813fc60c
  observations: 2

Findings (1):

1. [warning] Large unattributed interval between gateway and service
   confidence: likely   scope: gateway_to_upstream   rule: alloy.r003 v1
   The gateway measured 900.0 ms from backend dispatch to response
   headers; the service time-to-headers was 120.0 ms. 780.0 ms (87%)
   is unattributed: neither producer measured it.
   Evidence:
     - gateway_telemetry gateway.latency.backend_ttfb_ms = 900.0 ms
     - service_telemetry alloy.server.time_to_headers = 120.0 ms
   Does not prove:
     - network latency
     - that the service was idle during the interval
   Next checks:
     - gateway retry logs for this request
Abridged output for the repository's unattributed-interval contract fixture.

The ferrum-alloy Command

CommandPurpose
ferrum-alloy newCreates a starter project that compiles and passes its own tests, with optional openapi, postgres, jwt, http-client, otel, edge and tls
ferrum-alloy checkValidates configuration without starting the service
ferrum-alloy openapi exportExports the OpenAPI spec and detects drift
ferrum-alloy edge exportExports Ferrum Edge file-mode YAML (checked in CI with ferrum-edge validate) or a GitForgeOps tree. Nothing is applied to a gateway.
ferrum-alloy diagnoseExplains evidence deterministically, from files or from one request's live report

Crates and Cargo Features

Take the whole toolkit or just the part you need. Every battery is an opt-in Cargo feature.

ferrum-alloyAlloyApp, config, server, problems, health, limits
ferrum-alloy-telemetryTower/Axum instrumentation, usable alone
ferrum-alloy-edgeEdge contracts, trust modes, config export
ferrum-alloy-diagnosticsEvidence schema, parser, deterministic rules
ferrum-alloy-cliThe ferrum-alloy command
otel tls edge postgres openapi openapi-ui jwt http-client compression cors diagnostics full

Quick Start

Alloy is not on crates.io. Depend on it by git revision and pin a commit for reproducible builds.

Rust 1.94+ Cargo Ferrum Edge v0.9.10 / v0.9.9 (optional)
  1. Install the CLI and generate a service

    bash
    cargo install --git https://github.com/ferrum-edge/ferrum-alloy --rev 836b61cfb7e58d026cc659fb5152b59949eba327 ferrum-alloy-cli
    ferrum-alloy new orders-api --with openapi --alloy-rev 836b61cfb7e58d026cc659fb5152b59949eba327
    cd orders-api
    cargo test
    cargo run

    The project contains ordinary Axum handlers, alloy.toml, a service manifest for gateway configuration, handler tests and a GitHub Actions workflow.

  2. Or add Alloy to an existing project

    toml
    [dependencies]
    ferrum-alloy = { git = "https://github.com/ferrum-edge/ferrum-alloy", rev = "836b61cfb7e58d026cc659fb5152b59949eba327" }
  3. Put it behind Ferrum Edge

    bash
    ferrum-alloy edge export --manifest ferrum-service.toml --output edge.yaml
    ferrum-edge validate -m file -c edge.yaml

    Build with the tls and edge features and trust the gateway's SPIFFE identity. The getting started guide walks through the certificates and the examples/edge-observability stack.

Works With the Rest of the Stack

Licensing

Ferrum Alloy is licensed under PolyForm Noncommercial 1.0.0; commercial licenses are available. "Ferrum Alloy" and the crate and command names are working names. The bundled Swagger UI assets keep their own Apache-2.0 terms.