Application State Management
Relevant source files
The following files were used as context for generating this wiki page:
Application State Management in Hodor centralizes configuration, security assets, and runtime components required to process proxy requests. The system transforms static configuration into an immutable AppState struct shared across all request handlers via Axum's state management.
The state management lifecycle begins during application startup in main.rs, where the system loads raw configuration, validates templates, and initializes shared resources like the HTTP client and brute-force protection guards.
Sources: src/main.rs:25-30, src/state.rs:43-51, AGENTS.md:18-20
State Architecture
The AppState struct serves as the primary data container for the application. It holds sensitive data like the hashed password and session secrets, as well as operational parameters like timeout durations and CIDR blocks for network bypass.
The diagram shows the composition of the AppState struct and its relationship with shared runtime guards.
Sources: src/state.rs:18-41
Data Components
| Field | Type | Description |
|---|---|---|
password | Vec<u8> | The raw password bytes used for constant-time comparison. |
secret | Vec<u8> | The key used to sign and verify HMAC-SHA256 session cookies. |
upstream | Uri | The parsed backend destination address. |
login_guard | Arc<Mutex<LoginGuard>> | Thread-safe in-memory storage for rate limiting and IP lockouts. |
client | Client | A reusable Hyper HTTP client for streaming requests to the upstream. |
Sources: src/state.rs:19-40, AGENTS.md:27-31
Initialization Flow
The build_app_state function constructs the runtime state from a Config object. This process includes parsing URIs, loading templates from disk, and initializing the connection pool.
This flow illustrates the sequential transformation of configuration into operational state.
Sources: src/state.rs:43-91, src/main.rs:25-50
Template Validation
During initialization, the system loads template sources from the filesystem or uses built-in defaults. It renders a test page for both the login and error templates. If rendering fails due to syntax errors, the application panics immediately to prevent runtime failures.
Sources: src/state.rs:56-68, src/templates.rs:14-30
Connection Management
The state includes a pre-configured hyper_util Client. The system sets the upstream_connect_timeout during the connector initialization. This client is shared across all threads to manage connection pooling efficiently.
Sources: src/state.rs:71-74, AGENTS.md:38-39
Shared Runtime State
The LoginGuard is the only mutable component within the application state, wrapped in an Arc<Mutex<_>>. It tracks login attempts per IP address to provide brute-force protection.
- Rate Limiting: Limits IPs to 5 attempts per 60 seconds.
- Escalating Lockouts: Doubles lockout duration after 10 consecutive failures.
- Cleanup: Prunes old entries when the map reaches 10,000 tracked IPs.
Sources: src/state.rs:88-89, AGENTS.md:27-31, src/auth.rs:70-75
Usage in Handlers
The AppState is injected into Axum handlers using the State extractor. Because AppState implements Clone, Axum can provide a handle to the shared state for every incoming request.
// Example of state usage in auth handlers
pub(crate) async fn logout(State(state): State<AppState>) -> Response<Body> {
let mut response = Redirect::to("/").into_response();
match HeaderValue::from_str(&clear_cookie(&state)) {
Ok(value) => {
response.headers_mut().insert(SET_COOKIE, value);
response
}
Err(_) => internal_server_error(&state),
}
}
Sources: src/auth.rs:47-56, src/main.rs:20-23
Authentication State Access
Handlers access the state.secret to validate session tokens and state.password to verify login credentials. The proxy_or_login fallback uses state-provided bypass_paths and bypass_cidrs to determine if a request requires the password gate.
Sources: src/auth.rs:87-93, src/proxy.rs:60-75
Summary
Application State Management in Hodor ensures that all security parameters, templates, and networking components are validated at startup. By encapsulating these in a shared AppState, the system maintains a high-performance, thread-safe environment for the reverse proxy while centralizing the logic for brute-force protection and session management.