# Route-Switcher API Design ## Overview HTTP REST API with Basic Authentication for Home Assistant integration, exposing state machine state and ping statistics. ## Design Principles - **Minimal surface area**: Only expose necessary information - **Simple authentication**: HTTP Basic Auth (no JWT complexity) - **State-focused**: Centered on state machine state and ping history - **Home Assistant friendly**: Structured for HA REST integration - **Opt-in**: API disabled by default ## API Endpoints ### GET /api/state Returns current state machine state with ping statistics. **Response:** ```json { "state": "Primary", "primary_stats": { "success_rate": 95.5, "failures": 2, "total_pings": 44, "last_ping": "Ok" }, "secondary_stats": { "success_rate": 98.2, "failures": 1, "total_pings": 56, "last_ping": "Ok" }, "last_failover": "2024-02-15T10:30:00Z" } ``` **Fields:** - `state`: Current state machine state (Boot/Primary/Fallback) - `primary_stats`: Ping statistics for primary interface - `secondary_stats`: Ping statistics for secondary interface - `last_failover`: ISO 8601 timestamp of last failover (null if never) ### POST /api/state Manually set state machine state. **Request:** ```json { "state": "fallback" } ``` **Response:** ```json { "state": "Fallback", "previous_state": "Primary", "primary_stats": { ... }, "secondary_stats": { ... }, "last_failover": "2024-02-15T10:30:00Z" } ``` **Valid states:** `primary`, `fallback` ## Authentication HTTP Basic Authentication with username/password configured via environment variables. **Security considerations:** - Passwords stored as bcrypt hash - HTTPS recommended for production - Local network access only - No token management (stateless) ## Data Structures ### PingStats Calculated from state machine ping history (60 entries per interface): ```rust struct PingStats { success_rate: f64, // Percentage of successful pings failures: usize, // Number of failed pings in history total_pings: usize, // Total pings in history last_ping: String, // "Ok" or "Failed" } ``` ### StateResponse ```rust struct StateResponse { state: String, primary_stats: PingStats, secondary_stats: PingStats, last_failover: Option, } ``` ## Home Assistant Integration ### REST Sensor Configuration ```yaml sensor: - platform: rest name: Route Switcher State resource: http://route-switcher.local:8080/api/state username: !secret route_switcher_user password: !secret route_switcher_pass value_template: "{{ value_json.state }}" json_attributes: - primary_stats - secondary_stats - last_failover - platform: template sensors: route_switcher_primary_success_rate: value_template: "{{ state_attr('sensor.route_switcher_state', 'primary_stats').success_rate | default(0) }}" unit_of_measurement: "%" route_switcher_secondary_success_rate: value_template: "{{ state_attr('sensor.route_switcher_state', 'secondary_stats').success_rate | default(0) }}" unit_of_measurement: "%" route_switcher_primary_failures: value_template: "{{ state_attr('sensor.route_switcher_state', 'primary_stats').failures | default(0) }}" route_switcher_secondary_failures: value_template: "{{ state_attr('sensor.route_switcher_state', 'secondary_stats').failures | default(0) }}" switch: - platform: rest name: Route Switcher Control resource: http://route-switcher.local:8080/api/state username: !secret route_switcher_user password: !secret route_switcher_pass body_on: '{"state": "fallback"}' body_off: '{"state": "primary"}' is_on_template: "{{ value_json.state == 'fallback' }}" ``` ## Configuration ### Environment Variables ```bash # API Configuration API_ENABLED=true API_BIND_ADDRESS=0.0.0.0 API_PORT=8080 API_USERNAME=admin API_PASSWORD_HASH= # CORS Configuration API_CORS_ORIGINS=http://homeassistant.local:8123 ``` ### Password Hash Generation ```bash # Generate bcrypt hash echo -n "your-password" | bcrypt ``` ## Implementation Details ### Dependencies ```toml axum = "0.7" tokio = { version = "1.42", features = ["full"] } tower = "0.4" tower-http = { version = "0.5", features = ["cors", "auth"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" chrono = { version = "0.4", features = ["serde"] } bcrypt = "0.15" base64 = "0.22" ``` ### Architecture - **API Module**: `src/api.rs` - HTTP server and endpoints - **State Sharing**: Thread-safe access to state machine and ping history - **Authentication**: Basic Auth middleware with bcrypt validation - **Error Handling**: Standardized JSON error responses - **Integration**: Minimal changes to existing state machine ### Thread Safety - `Arc>` for shared state access - Non-blocking async operations - Minimal locking duration ## Error Handling Standardized error responses: ```json { "error": "Invalid state", "message": "State must be 'primary' or 'fallback'" } ``` HTTP Status Codes: - 200: Success - 400: Bad Request (invalid state) - 401: Unauthorized (invalid credentials) - 500: Internal Server Error ## Security Considerations - Network access restrictions (local only recommended) - HTTPS for credential protection - Rate limiting considerations - Audit logging for manual state changes - No configuration exposure (state only) ## Backward Compatibility - API disabled by default - No changes to existing CLI functionality - Service continues without API if disabled - Graceful degradation on API errors