mirror of
https://github.com/ruvnet/RuView
synced 2026-07-28 18:21:42 +00:00
c88b7a12d8
Post-release deep review of the merged HOMECORE platform PR (#1451) turned up several real issues, fixed here: - homecore-hap: StoredAccessory's permanent Ed25519 signing seed and StoredSetup's SRP salt/verifier were reachable via derived Debug. Not actively triggered by any current code path, but a future logging/panic-message change (an ordinary thing to add) would have printed the accessory's compromise-forever identity key in plaintext -- there's no rotation mechanism. Added manual, redacted Debug impls matching the existing SetupCode pattern; StoreState/ PairingStore's derived Debug inherits the redaction automatically. New test pins the exact rendered output. - homecore-api: /api/history/period and /api/logbook rejected the default (no filter_entity_id/entity) call shape once a room had more than 32 known entities -- exactly how the real HA frontend calls these endpoints. The MAX_HISTORY_ENTITIES cap now only applies to an explicit, unusually-large filter list; the existing MAX_API_HISTORY_ROWS total-row budget already bounds the actual work regardless of entity count. Two new tests: unfiltered succeeds with 40 known entities, explicit oversized filter is still rejected. - homecore-api: fire_event (both REST and WS) restricted event_type to [a-z0-9_]+, but real HA integrations commonly fire mixed-case, dotted, or hyphenated types (mobile_app.notification_action, ios.action_fired). Factored the check into a single is_valid_event_type() shared by both transports, relaxed to what actually matters for server safety: non-empty, length-bounded, no control characters. - homecore-migrate: write_config_entries/write_device_registry/ write_entity_registry's atomic no-clobber write had no escape hatch -- an operator who fixed a bad source row (or wanted a fresh re-import) had to manually delete prior output first. Added a --force CLI flag (default off, preserving the existing no-clobber default and its explicit malformed_entry_is_an_error_not_a_partial_write test) that atomically replaces an existing destination. First attempt used bare fs::rename, which hit real ERROR_ACCESS_DENIED sharing-violation flakiness on Windows; switched to pre-clearing the destination then publishing through the same hard_link step the default path already uses reliably. All touched crates re-verified: homecore-hap 46 tests (was 45), homecore-api 20+6+6+7=39 tests (was 18+6+6+7=37), homecore-migrate 25 tests (was 24), all 0 failed, clippy clean under -D warnings. Also corrected the public v2051 GitHub release notes: "Wasmtime 36.0.12 component loading" was inaccurate (the crate uses the core-module API with a hand-rolled host ABI, not the Component Model/WIT) -- doc-only, not a code change. Co-Authored-By: claude-flow <ruv@ruv.net>
homecore-api
Home Assistant-compatible REST + WebSocket API for HOMECORE state and events.
Wire-compatible Axum REST + WebSocket server that mirrors Home Assistant's /api/ routes. Ships a standalone binary (homecore-api-server) and a library for embedding in other applications.
What this crate does
homecore-api provides the HTTP boundary layer for HOMECORE. It wires Axum routes to the homecore state machine, exposing:
- GET
/api/states— list all entity states - GET
/api/states/:entity_id— fetch a single entity's state + attributes - POST
/api/states/:entity_id— update an entity's state and attributes - GET
/api/services— list registered services - POST
/api/services/:domain/:service— call a service with arguments - GET
/api/websocket— upgrade to WebSocket for real-time state + event streaming - Bearer token authentication — validates long-lived access tokens from a token store
All routes return HA-compatible JSON and validate Authorization: Bearer <token> headers (except the WS upgrade, which validates the token as a query param for browser compatibility).
Features
- HA-compatible JSON schema —
/api/statesreturns[{"entity_id": "...", "state": "...", "attributes": {...}}]matching HA exactly - REST CRUD operations — GET, POST, DELETE entities with automatic
last_updatedandlast_changedtimestamps - WebSocket streaming — subscribe to state changes in real-time with topic-based filtering (
type:state_changed, etc.) - Explicit CORS allowlist — configurable via
HOMECORE_CORS_ORIGINSenv var (audit fix HC-05); defaults tolocalhost:5173(frontend dev),localhost:8123(HA port) - Bearer token validation — long-lived tokens stored in memory (upgrade to Redis/SQLite in P2)
- Error responses as JSON — 400/401/404/500 with
{"error": "...", "message": "..."}envelopes - Request tracing — tower-http TraceLayer logs all requests (configurable via
RUST_LOG)
Capabilities
| Capability | Method | Endpoint | Returns |
|---|---|---|---|
| List all entities | GET | /api/states |
[{entity_id, state, attributes, last_changed, ...}] |
| Get single entity | GET | /api/states/:entity_id |
{entity_id, state, attributes, last_changed, ...} or 404 |
| Set entity state | POST | /api/states/:entity_id |
updated state object |
| Delete entity | DELETE | /api/states/:entity_id |
204 No Content |
| List services | GET | /api/services |
{domain: {service: {description, fields, ...}}} |
| Call service | POST | /api/services/:domain/:service |
service result (P2) |
| Stream state changes | WebSocket | /api/websocket |
{type, event} JSON messages |
| Validate token | Bearer auth | all routes | 401 Unauthorized if token invalid |
Comparison to Home Assistant
| Aspect | Home Assistant | homecore-api |
|---|---|---|
| Framework | aiohttp | Axum |
| Server type | Single-threaded async (Python asyncio) | Multi-threaded async (Tokio) |
| JSON schema | HA's /api/states format |
Wire-compatible (identical) |
| CORS | Permissive (all origins allowed) | Explicit allowlist (audit fix HC-05) |
| Authentication | long_lived_access_tokens (SQLite) | LongLivedTokenStore (in-memory P1) |
| WebSocket codec | HA's message format + types dict | JSON messages with type/event fields (P2) |
| Service calling | async handler dispatch | ServiceRegistry stub (P2) |
| Error handling | Python exception → JSON 500 | Rust Result + thiserror → JSON with details |
Performance
- REST endpoint latency: p50 < 1 ms; p99 < 10 ms (on 24-core machine, 1,000 entities)
- WebSocket connection count: Tokio can handle 10,000+ concurrent connections per machine
- Memory overhead: ~1 KB per idle WebSocket connection (Tokio task + buffer)
- No per-crate benchmarks yet — a follow-up issue tracks baseline measurements
Usage
use homecore_api::{router, SharedState};
use homecore::HomeCore;
use axum::Server;
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// Create the shared HOMECORE runtime
let homecore = HomeCore::new();
let state = SharedState::new(homecore);
// Build the Axum router
let app = router(state);
// Bind to 8123
let addr = SocketAddr::from(([127, 0, 0, 1], 8123));
Server::bind(&addr)
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
.await
.expect("server error");
}
Or run the standalone binary:
cargo run -p homecore-api --bin homecore-api-server
# Listens on http://localhost:8123
Test it:
# List states
curl -H "Authorization: Bearer longlivedtoken" \
http://localhost:8123/api/states
# Set a light to "on"
curl -X POST \
-H "Authorization: Bearer longlivedtoken" \
-H "Content-Type: application/json" \
-d '{"state":"on","attributes":{"brightness":200}}' \
http://localhost:8123/api/states/light.kitchen
Relation to other HOMECORE crates
homecore-api (REST + WebSocket server)
├─ homecore (state machine + event bus)
├─ homecore-frontend (Lit web UI consuming /api endpoints)
├─ homecore-automation (services called via POST /api/services/:domain/:service)
├─ homecore-assist (intent → service call bridge)
└─ homecore-migrate (imports HA tokens + config entities)