Claude Opus Council
Judge 1: ACCURACY AUDITOR
-
[HIGH] API Reference documents all state-setting endpoints as
POST. The actual source code usesPUTfor all of these:fan/routes.py:119,fan/routes.py:147,led/routes.py:147,led/routes.py:175,oled/routes.py:123,oled/routes.py:155. Anyone following the docs will get 405 Method Not Allowed errors. -
[HIGH] API Reference shows health endpoint version as
"version": "0.1.0". Bothpyproject.toml(version = "0.1.0") andcasectl/__init__.py(__version__ = "0.1.0") are wrong. The MQTT plugin already self-identifies as version “0.2.0”, so there is an internal version inconsistency. -
[HIGH] Architecture page lists only 5 plugins. The
pyproject.tomlentry points show 8 plugins: those 5 plusmqtt,automation, andalerting. The architecture page also marks “TUI” as “soon” whencasectl topis already implemented. -
[HIGH] CLI Reference page is missing the
casectl topcommand entirely. This is a major feature – an interactive terminal dashboard with live updates, interactive key bindings, and--oncemode for scripting. -
[MEDIUM] API Reference documents fan mode values as
{"mode": 0}(integer body). The actualSetFanModeRequestacceptsint | str, meaning both{"mode": 0}and{"mode": "follow-temp"}are valid. Docs only show integers. -
[MEDIUM] API Reference is missing the OLED
/powerand/contentendpoints, all MQTT plugin endpoints, all automation plugin endpoints, all alerting plugin endpoints, and the SSE endpoint (GET /api/sse). -
[MEDIUM] Configuration page is missing
mqtt,automation, andfan.curveconfig sections. Thealertssection is missing 8 fields added in v0.2.0. -
[LOW] WebSocket section documents only read-only streaming. The actual WebSocket supports 8 bidirectional commands. WebSocket auth via query param is undocumented.
-
[LOW] CLI Reference
config getdocuments sections asfan, led, oled, service, alerts– missingmqttandautomation.
Judge 2: SECURITY REVIEWER
-
[HIGH] MQTT credentials (
passwordfield inMqttConfig) are stored in plaintext inconfig.yamlwith no redaction.GET /api/config/mqttreturns the MQTT broker password in the API response. Unlikealerts.smtp_passwordwhich is redacted,mqtt.passwordis exposed. -
[HIGH] The ntfy.sh access token (
ntfy_token) is stored in plaintext and returned unredacted viaGET /api/config/alerts. -
[MEDIUM] SMTP credentials are sent over STARTTLS but there is no explicit certificate verification. Consider using
ssl.create_default_context(). -
[MEDIUM] The
tls_insecureoption in MQTT config setscheck_hostname = Falseandverify_mode = CERT_NONE. No warning log is emitted. -
[MEDIUM] Webhook URL accepts any URL scheme including
file://,ftp://, etc. – potential SSRF viahttpx.AsyncClient. Should validate tohttp://orhttps://only. -
[LOW] API token first 8 characters are logged at WARNING level. Could expose token prefixes in centralized logging.
-
[LOW] WebSocket only checks query parameter
?token=– no cookie or header auth. Weaker than HTTP auth. -
[LOW]
config.yamlpermissions are not explicitly set to0o600. Contains plaintext secrets.
Judge 3: ARCHITECTURE CRITIC
-
[MEDIUM]
mqtt/state.pyat 1273 lines handles too many concerns: fan/LED/OLED state publishing, command handling, HA discovery, colour parsing, mode mapping. Should be split into per-device modules. -
[MEDIUM] Mode name-to-integer mappings duplicated in 5+ places: CLI, server, fan routes, LED routes, MQTT state. A single canonical mapping in
config/models.pywould eliminate drift risk. -
[MEDIUM] CLI uses
_api_post()for OLED screen and rotation but server routes usePUT. The CLI and server are not sharing a contract. -
[LOW] Plugin cross-wiring uses
app.statewith string keys. Implicit coupling, but works and is documented. -
[LOW]
AlertDispatcher.dispatch()cooldown is implemented in the plugin, not the dispatcher. Split responsibility is confusing. -
[LOW] Inconsistent config loading: automation reads from
ctx.get_config()(plugins section), alerting reads fromctx.config_manager.get("alerts")(top-level).
Judge 4: API DESIGN JUDGE
-
[HIGH] HTTP method mismatch between CLI and server. The CLI sends
POSTfor OLED screen and rotation, but the server definesPUT. These CLI commands return 405. -
[MEDIUM] POST-to-PUT migration is incomplete. Fan/LED routes use PUT (correct), but CLI OLED still sends POST, and all docs show POST.
-
[MEDIUM] Fan speed API accepts 0-100 range but status returns 0-255 hardware range. Writing
{"duty": [75]}then reading status showsduty: [191]. -
[MEDIUM] WebSocket bidirectional command protocol is completely undocumented. 8 command types exist but are invisible to API consumers.
-
[LOW] Response key
duty_hwleaks internal “hardware” abstraction into the API response. -
[LOW] SSE and WebSocket serve overlapping purposes; their relationship is not documented.
Judge 5: DOCUMENTATION COMPLETENESS
-
[HIGH]
casectl topis completely undocumented. Supports interactive keys (m, +/-, q) and--oncefor scripting. -
[HIGH] Three entire plugins are undocumented: MQTT (HA auto-discovery, 12 command topics), automation (rule engine with priority/cooldown), alerting (webhook, ntfy.sh, SMTP).
-
[HIGH] Configuration page missing
mqtt,automation, andfan.curvesections. Thealertssection is missing 8 fields. -
[HIGH] SSE endpoint (
GET /api/sse) is completely undocumented. -
[MEDIUM]
aiomqttmissing from pyproject.toml dependencies. Users cannot install it without reading the source. -
[MEDIUM] Plugin development page references only 3 events; the codebase emits many more.
-
[LOW] Architecture diagram still shows TUI as “soon” when it is implemented.
Consolidated Report
Critical Issues (must fix)
- CLI/Server HTTP method mismatch (runtime bug): CLI sends POST for OLED, server expects PUT. Commands return 405.
- API docs show POST, server uses PUT: Every curl example will fail with 405.
- Version not bumped to 0.2.0: pyproject.toml and
__init__.pyboth say 0.1.0. - MQTT password exposed via API:
GET /api/config/mqttreturns password unredacted.
Important Issues (should fix)
- Three new plugins completely undocumented (MQTT, automation, alerting)
casectl topundocumented- Configuration page missing mqtt, automation, fan.curve sections
- SSE endpoint undocumented
aiomqttmissing from pyproject.toml dependencies- Fan duty units inconsistent (0-100 API vs 0-255 status)
- Webhook URL allows arbitrary schemes (SSRF)
- Architecture page outdated (5/8 plugins, TUI “soon”)
Positive Observations
- Excellent plugin architecture: Protocol-based structural subtyping, entry-point discovery
- Robust MQTT implementation: TLS, exponential backoff reconnection, LWT, birth messages, HA auto-discovery
- Sound automation engine: Priority-based conflict resolution, cooldown tracking, sub-500ms latency
- Good auth design: Timing-safe comparison, cookie sessions, localhost bypass, proxy trust
- SSE implementation is excellent: Heartbeat keepalives, backpressure, max client limits
- Test suite growth from 194 to 1193 tests