High-Level Architecture
Relevant source files
The following files were used as context for generating this wiki page:
Hodor is a lightweight reverse proxy written in Rust that secures web applications behind a single shared password. It functions as a Docker sidecar, requiring no database or complex OAuth setup. The system provides a streaming reverse proxy, brute-force protection, and customizable login interfaces.
Sources: README.md:3-8, AGENTS.md:7-8
Core System Components
The Hodor architecture comprises several focused modules that handle configuration, state management, authentication, and request proxying. The server utilizes the axum framework and hyper for low-level HTTP operations.
Component Overview
| Component | Responsibility | Relevant Files |
|---|---|---|
| Config Loader | Processes layered configuration from defaults, hodor.toml, and environment variables. | src/config.rs |
| App State | Maintains runtime shared state including HTTP clients and security secrets. | src/state.rs |
| Proxy Engine | Manages streaming request/response flows and WebSocket upgrades. | src/proxy.rs |
| Auth Handler | Handles login, logout, and session validation. | src/main.rs, AGENTS.md |
| Template Engine | Renders HTML login and error pages using minijinja. | src/templates.rs |
Sources: AGENTS.md:22-26, src/main.rs:18-24
Shared Runtime State
The AppState struct represents the core runtime context shared across all request handlers. It includes the password (stored as Vec<u8>), the secret for signing session cookies, and the client used for upstream communication.
The diagram shows the transition from static configuration to the live AppState used by the proxy.
Sources: src/state.rs:20-44, src/state.rs:46-95
Request Flow and Logic
Hodor evaluates every incoming request through a series of gates to determine if it should be proxied to the UPSTREAM or intercepted for authentication.
Authentication Pipeline
When a request arrives, Hodor checks for bypass conditions before enforcing password authentication.
- Health Check: Requests to
/_gate/healthbypass all checks to report process liveness. - Public Paths: Requests matching
BYPASS_PATHS(configured inConfig) bypass authentication. - Network Trust: Requests from IP addresses within
BYPASS_CIDRSbypass authentication. - Session Validation: Hodor validates the HMAC-SHA256 signature of the session cookie.
- Interception: If all checks fail, Hodor serves the login page.
The flow diagram illustrates the decision matrix Hodor uses to authorize or challenge a request.
Sources: AGENTS.md:13-20, src/proxy.rs:48-67, README.md:126-137
Reverse Proxy Implementation
The proxy engine is a streaming implementation that avoids buffering large request or response bodies in memory. This supports high-performance file transfers and WebSocket connections.
Header Management
Hodor modifies HTTP headers to ensure security and provide context to the upstream application:
- Stripping: It removes client-supplied
X-Hodor-*headers to prevent authentication spoofing. - Identification: It injects
X-Hodor-Authto inform the upstream how the request was cleared (values:password,bypass, orpublic). - Forwarding: It appends
X-Forwarded-ForandX-Forwarded-Protoheaders. - Static Headers: If
UPSTREAM_HEADERis configured, it replaces client values for that specific header.
Sources: src/proxy.rs:141-154, src/proxy.rs:189-202, README.md:175-188
WebSocket Proxying
For requests initiating a WebSocket upgrade, Hodor performs a bidirectional tunnel between the downstream client and the upstream server. It uses tokio::io::copy_bidirectional to manage the raw stream once the HTTP handshake completes.
Sources: src/proxy.rs:114-119, src/proxy.rs:214-226
Security and Brute-Force Protection
Hodor implements a per-IP rate limiting system to mitigate password guessing attempts.
- Rate Limiting: Limits attempts to 5 per 60 seconds per IP.
- Escalating Lockout: After 10 consecutive failures, Hodor doubles the lockout time for each subsequent failure, capped at 1 hour.
- Delayed Responses: Failed login attempts incur a 500ms delay to slow automated attacks.
- In-Memory Tracking: Protection state is stored in an
Arc<Mutex<LoginGuard>>within theAppState, supporting up to 10,000 tracked IPs.
Sources: AGENTS.md:27-37, README.md:139-152, src/state.rs:43
Template System
The system uses minijinja to provide dynamic HTML responses for the login gate and upstream error states.
Rendering Logic
Hodor loads templates from built-in HTML strings (src/template.html) or external files defined in the configuration. The render_login_page and render_error_page functions inject variables such as title, custom_css, and show_error into the templates.
Sources: src/templates.rs:45-70, src/templates.rs:9-12
Summary
The Hodor architecture focuses on a "fail-closed" security model. It combines a streaming proxy based on axum and hyper with an in-memory security layer that protects against unauthorized access and brute-force attacks. By leveraging Rust's safety and performance, Hodor maintains a small footprint (approx. 5MB Docker image) while handling complex proxy tasks like WebSockets and large file streams.