This document describes the architecture and design decisions of bmcweb-ng, a Rust rewrite of the OpenBMC webserver.
- Overview
- Design Principles
- Architecture Layers
- Component Details
- Data Flow
- Concurrency Model
- Error Handling
- Security
- Performance Considerations
bmcweb-ng is designed as a high-performance, memory-safe BMC webserver that implements the Redfish API specification. The architecture follows a layered approach with clear separation of concerns.
┌─────────────────────────────────────────────────────────────┐
│ Client Applications │
│ (Web UI, CLI tools, Management Software) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Protocol Layer │
│ HTTP/1.1, HTTP/2, HTTPS, WebSocket, TLS │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Authentication Layer │
│ Basic, Session, Cookie, mTLS, XToken │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ API Layer │
│ Redfish Resources, REST Endpoints, WebSocket │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Service Layer │
│ System, Chassis, Manager, Session, Event Management │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DBus Layer │
│ Abstraction over OpenBMC DBus Services │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ OpenBMC Services │
│ phosphor-*, xyz.openbmc_project.* DBus Services │
└─────────────────────────────────────────────────────────────┘
Each layer has a specific responsibility and communicates only with adjacent layers. This promotes:
- Separation of concerns: Each layer focuses on one aspect
- Testability: Layers can be tested independently with mocks
- Maintainability: Changes in one layer don't affect others
- Flexibility: Layers can be swapped or extended
Components receive their dependencies through constructor injection:
pub struct RedfishService {
dbus_client: Arc<dyn DBusClient>,
config: Arc<Config>,
}
impl RedfishService {
pub fn new(dbus_client: Arc<dyn DBusClient>, config: Arc<Config>) -> Self {
Self { dbus_client, config }
}
}Benefits:
- Easy to mock dependencies for testing
- Clear dependency graph
- Supports multiple implementations
Key interfaces are defined as traits:
#[async_trait]
pub trait DBusClient: Send + Sync {
async fn get_property(&self, path: &str, interface: &str, property: &str)
-> Result<Value>;
async fn set_property(&self, path: &str, interface: &str, property: &str, value: Value)
-> Result<()>;
async fn call_method(
&self, destination: &str, path: &str, interface: &str, method: &str,
args: Option<&Value>,
) -> Result<Value>;
}This enables:
- Multiple implementations (real, mock, test)
- Runtime polymorphism
- Clear contracts between components
All I/O operations use async/await for efficient concurrency:
- Non-blocking I/O
- Efficient resource utilization
- Scalable to many concurrent connections
- Clean, readable code compared to callbacks
Leverage Rust's type system for correctness:
- Strong typing prevents many bugs at compile time
Result<T, E>for error handlingOption<T>for nullable values- Newtype pattern for domain types
Responsibility: Handle low-level network protocols
Components:
- HTTP server (HTTP/1.1, HTTP/2)
- TLS configuration and certificate management
- WebSocket protocol handling
- Connection management
Technologies:
tokio- Async runtimehyper- HTTP implementationaxum- Web frameworktokio-rustls- TLS supporttokio-tungstenite- WebSocket support
Key Features:
- HTTP/2 with ALPN negotiation
- TLS 1.3 support
- Automatic certificate generation
- Connection pooling and keep-alive
- Request/response compression
Responsibility: Authenticate and authorize requests
Components:
- Basic authentication (RFC 7617)
- Session-based authentication
- Cookie authentication
- Mutual TLS (mTLS)
- XToken authentication (Redfish)
- Privilege checking
Flow:
Request → Extract Credentials → Validate → Check Privileges → Allow/Deny
Session Management:
- In-memory session store
- Configurable timeout
- Session token generation
- Concurrent session limits
Responsibility: Expose HTTP endpoints and handle routing
Components:
- Redfish resource handlers (
src/api/redfish/) - WebSocket handlers (
src/api/websocket/) - REST API endpoints
- Request validation
- Response formatting
Routing:
Router::new()
.route("/redfish/v1", get(service_root))
.route("/redfish/v1/Systems", get(systems_collection))
.route("/redfish/v1/Systems/:id", get(system_instance))
.route("/redfish/v1/Chassis", get(chassis_collection))
// ... more routesResponsibility: Business logic and resource management
Components:
- System management
- Chassis management
- Manager resources
- Session management
- Event service
- Task service
- Update service
Pattern:
pub struct SystemService {
dbus: Arc<dyn DBusClient>,
}
impl SystemService {
pub async fn get_system(&self, id: &str) -> Result<System> {
// 1. Validate input
// 2. Query DBus for system information
// 3. Transform to Redfish format
// 4. Return result
}
}Responsibility: Abstract DBus communication
Components:
- DBus client trait
- zbus implementation
- Mock implementation for testing
- Connection pooling
- Error mapping
Abstraction:
#[async_trait]
pub trait DBusClient: Send + Sync {
async fn get_property(&self, path: &str, interface: &str, property: &str)
-> Result<Value>;
// ... other methods
}
pub struct ZBusClient {
connection: Connection,
}
#[async_trait]
impl DBusClient for ZBusClient {
async fn get_property(&self, path: &str, interface: &str, property: &str)
-> Result<Value> {
// Implementation using zbus
}
}Responsibility: Application configuration management
Features:
- TOML-based configuration
- Environment variable overrides
- Command-line argument parsing
- Configuration validation
- Default values
Structure:
#[derive(Debug, Deserialize)]
pub struct Config {
pub server: ServerConfig,
pub auth: AuthConfig,
pub features: FeatureConfig,
pub logging: LoggingConfig,
pub metrics: MetricsConfig,
}Responsibility: Logging, metrics, and tracing
Components:
- Structured logging (tracing)
- Prometheus metrics
- OpenTelemetry tracing
- Health checks
Metrics:
- Request count and latency
- Active connections
- Authentication attempts
- DBus call statistics
- Error rates
1. Client Request
↓
2. TLS Termination (if HTTPS)
↓
3. HTTP Parsing
↓
4. Authentication Middleware
↓
5. Authorization Check
↓
6. Route Matching
↓
7. Handler Execution
↓
8. Service Layer Call
↓
9. DBus Query
↓
10. Response Formatting
↓
11. Compression (if supported)
↓
12. Send Response
1. HTTP Upgrade Request
↓
2. Authentication
↓
3. WebSocket Handshake
↓
4. Persistent Connection
↓
5. Bidirectional Messages
↓
6. Event Streaming / KVM / Serial
bmcweb-ng uses the Tokio async runtime:
- Multi-threaded work-stealing scheduler
- Efficient task scheduling
- Non-blocking I/O
- Cooperative multitasking
Shared state is managed using:
Arc<T>for shared ownershipRwLock<T>for read-write accessMutex<T>for exclusive access- Atomic types for simple counters
Example:
pub struct AppState {
pub config: Arc<Config>,
pub dbus_connection: Option<Arc<Connection>>,
pub system_uuid: String,
pub session_store: Option<Arc<SessionStore>>,
pub metrics: Option<Arc<Metrics>>,
pub event_service: Option<Arc<EventService>>,
pub task_service: Option<Arc<TaskService>>,
pub update_service: Option<Arc<UpdateService>>,
}Long-running operations are spawned as separate tasks:
tokio::spawn(async move {
// Long-running operation
process_event_subscription(subscription).await;
});-
Application Errors (
anyhow::Error):- Used in application code
- Provides context and backtraces
- Easy error propagation with
?
-
Library Errors (
thiserror):- Used in library code
- Structured error types
- Implements
std::error::Error
Example:
#[derive(Debug, thiserror::Error)]
pub enum DBusError {
#[error("Connection failed: {0}")]
ConnectionFailed(String),
#[error("Property not found: {path}:{interface}:{property}")]
PropertyNotFound {
path: String,
interface: String,
property: String,
},
#[error("Method call failed: {0}")]
MethodCallFailed(#[from] zbus::Error),
}pub async fn get_system_info(id: &str) -> Result<SystemInfo> {
let power_state = dbus.get_property(
&format!("/xyz/openbmc_project/state/host{}", id),
"xyz.openbmc_project.State.Host",
"CurrentHostState"
).await?; // Propagate error with ?
Ok(SystemInfo {
power_state: parse_power_state(&power_state)?,
// ...
})
}Multiple authentication methods supported:
- Basic Auth: Username/password via HTTP Basic (with PAM)
- Session Auth: Token-based sessions (X-Auth-Token header)
- Cookie Auth: Browser-friendly cookies (BMCWEB-SESSION)
- XToken: Redfish session tokens
Role-based access control (RBAC):
- Administrator
- Operator
- ReadOnly
- NoAccess
Privilege checking per endpoint:
#[derive(Debug, Clone, Copy)]
pub enum Privilege {
Login,
ConfigureManager,
ConfigureUsers,
ConfigureSelf,
ConfigureComponents,
}
pub fn check_privilege(session: &Session, required: Privilege) -> Result<()> {
if session.has_privilege(required) {
Ok(())
} else {
Err(Error::Forbidden)
}
}- TLS 1.3 preferred
- Strong cipher suites only
- Certificate validation
- Automatic certificate generation for development
All inputs are validated:
- Request body parsing with serde
- Path parameter validation
- Query parameter validation
- Size limits on requests
-
Connection Pooling:
- Reuse DBus connections
- HTTP keep-alive
- WebSocket connection reuse
-
Caching:
- Cache frequently accessed data
- Invalidate on changes
- TTL-based expiration
-
Async I/O:
- Non-blocking operations
- Concurrent request handling
- Efficient resource utilization
-
Zero-Copy:
- Use
Bytesfor buffer management - Avoid unnecessary allocations
- Stream large responses
- Use
-
Compression:
- gzip and zstd support
- Compress responses > 1KB
- Negotiate with client
Measured on OpenBMC qemuarm (emulated Cortex-A15, 256 MB RAM, 4 vCPUs) — July 2026.
All tests run against the ARM release binary (bmcwebd-ng v0.2.0, opt-level="z", LTO, stripped).
| Metric | Target | Measured | Status | Notes |
|---|---|---|---|---|
| Binary Size | <1MB | 4.75 MB | Dynamically-linked ARM EABI. Target was aspirational for a static musl build. Virtual size on disk is 4,984,796 bytes. | |
| Memory RSS (idle) | <10MB | 5.7 MB | ✅ Met | /proc/pid/status VmRSS after cold start with no active sessions. VmPeak=22.8 MB, VmSize=21.8 MB. |
| Startup Time | <1s | ~1.6s | Cold start on emulated ARM (QEMU is ~5–10× slower than bare metal). Expected <500ms on AST2600/AST2700. | |
| Request Latency (p99) | <100ms | 7ms | ✅ Met | 30 sequential GETs to /redfish/v1. p50=4ms, p95=5ms, p99=7ms. QEMU network stack included. |
| Concurrent Connections | 100+ | 20/20 ✅ | ✅ Partial | 20 simultaneous connections all succeeded (avg 175ms, max 964ms wall). Full 100-connection test pending on real hardware. |
| Throughput | 1000+ req/s | ~200 req/s | Estimated from concurrency test (20 req / 1.07s wall). QEMU CPU bottleneck; real hardware expected to exceed target. |
The <1MB goal assumed a fully static musl build. The current glibc dynamically-linked
release build is larger because:
tokio+hyper+axum+tower— async HTTP stack (~1.5 MB code)rustls+rcgen— TLS without OpenSSL (~0.8 MB)zbus+zvariant— DBus async runtime (~0.5 MB)serde_json+serde— JSON serialisation (~0.4 MB)tracing+prometheus— observability (~0.4 MB)
A future arm-unknown-linux-musleabihf static build with --no-default-features on
observability crates could bring this closer to 2–3 MB. The <1MB target has been
revised to <5MB as a realistic goal for the current feature set.
The 1.6s on QEMU breaks down as:
- Config loading + UUID read from disk: ~50ms
- DBus system bus connection: ~200ms
- tokio runtime init + axum router build: ~50ms
- TCP socket bind: <5ms
- QEMU emulation overhead: accounts for the majority of the remainder
On a real OpenBMC BMC (AST2600 @ 800 MHz) the non-emulation components sum to
roughly 300–500ms, which is within the <1s target.
Tools for performance analysis:
cargo flamegraph- CPU profilingvalgrind- Memory profilingperf- Linux performance analysistokio-console- Async runtime inspection
- HTTP/3 Support: QUIC-based HTTP
- GraphQL API: Alternative to REST
- gRPC Support: For internal services
- Distributed Tracing: Full request tracing
- Advanced Caching: Redis integration
- Rate Limiting: Per-user/IP rate limits
- API Versioning: Multiple API versions
- Plugin System: Extensible architecture
Future scalability improvements:
- Horizontal scaling with load balancer
- Distributed session storage
- Event streaming with Kafka
- Microservices architecture option