Lucien Cartier-Tilet 27cfeb3b77 docs: update README for Phase 3 infrastructure completion
Update README to reflect completed Phase 3 infrastructure layer:
- Documented ModbusRelayController, MockRelayController, SqliteRelayLabelRepository, and HealthMonitor implementations
- Added testing coverage details (20+ tests across infrastructure components)
- Updated architecture diagrams and project structure
- Changed task reference to tasks.org format
- Updated dependency list with production infrastructure dependencies

Ref: Phase 3 tasks in specs/001-modbus-relay-control/tasks.org
2026-01-22 01:15:27 +01:00
2026-01-11 00:39:18 +01:00
2026-01-11 00:39:18 +01:00
2026-01-11 00:39:18 +01:00
2026-01-11 00:39:18 +01:00

STA

Smart Temperature & Appliance Control

Coding Time Badge

🤖 AI-Assisted Development Notice: This project uses Claude Code as a development assistant for task planning, code organization, and workflow management. However, all code is human-written, reviewed, and validated by the project maintainer. AI is used as a productivity tool, not as the author of the implementation.

Web-based Modbus relay control system for managing 8-channel relay modules over TCP.

⚠️ Development Status: This project is in early development. Core features are currently being implemented following a specification-driven approach.

Overview

STA will provide a modern web interface for controlling Modbus-compatible relay devices, eliminating the need for specialized industrial software. The goal is to enable browser-based relay control with real-time status updates.

Current Status

Phase 1 Complete - Foundation

  • Monorepo structure (backend + frontend at root)
  • Rust web server with Poem 3.1 framework
  • Configuration system (YAML + environment variables)
  • Modbus TCP and relay settings structures
  • Health check and metadata API endpoints
  • OpenAPI documentation with Swagger UI
  • Rate limiting middleware
  • SQLite schema and repository for relay labels
  • Vue 3 + TypeScript frontend scaffolding with Vite
  • Type-safe API client generation from OpenAPI specs

Phase 0.5 Complete - CORS Configuration & Production Security

  • T009: CorsSettings struct with comprehensive unit tests (5 tests)
  • T010: CorsSettings implementation with restrictive fail-safe defaults
  • T011: Development YAML configuration with permissive CORS
  • T012: Production YAML configuration with restrictive CORS
  • T013: From for Cors trait unit tests (6 tests)
  • T014: From for Cors implementation with security validation
  • T015: Middleware chain integration using From trait
  • T016: Integration tests for CORS headers (9 comprehensive tests)

Key CORS Features Implemented

  • Environment-specific CORS configuration (development vs production)
  • Wildcard origin support for development (allowed_origins: ["*"])
  • Multiple specific origins for production
  • Credentials support for Authelia authentication
  • Security validation (prevents wildcard + credentials)
  • Configurable preflight cache duration
  • Hardcoded secure methods and headers
  • Structured logging for CORS configuration
  • Comprehensive test coverage (15 tests total)

Phase 2 Complete - Domain Layer (Type-Driven Development)

  • T017-T018: RelayId newtype with 1-8 validation and zero-cost abstraction
  • T019-T020: RelayState enum (On/Off) with serialization support
  • T021-T022: Relay aggregate with state control methods (toggle, turn_on, turn_off)
  • T023-T024: RelayLabel newtype with 1-50 character validation
  • T025-T026: ModbusAddress type with From trait (1-8 → 0-7 offset mapping)
  • T027: HealthStatus enum with state machine (Healthy/Degraded/Unhealthy)

Key Domain Layer Features Implemented

  • 100% test coverage for domain layer (50+ comprehensive tests)
  • Zero external dependencies (pure business logic)
  • All newtypes use #[repr(transparent)] for zero-cost abstractions
  • Smart constructors with Result<T, E> for type-safe validation
  • TDD workflow (red-green-refactor) for all implementations
  • RelayController and RelayLabelRepository trait definitions
  • Complete separation from infrastructure concerns (hexagonal architecture)

Phase 3 Complete - Infrastructure Layer

  • T028-T029: MockRelayController tests and implementation
  • T030: RelayController trait with async methods (read_state, write_state, read_all, write_all)
  • T031: ControllerError enum (ConnectionError, Timeout, ModbusException, InvalidRelayId)
  • T032: MockRelayController comprehensive tests (6 tests)
  • T025a-f: ModbusRelayController implementation (decomposed):
    • Connection setup with tokio-modbus
    • Timeout-wrapped read_coils and write_single_coil helpers
    • RelayController trait implementation
  • T034: Integration test with real hardware (uses #[ignore] attribute)
  • T035-T036: RelayLabelRepository trait and SQLite implementation
  • T037-T038: MockRelayLabelRepository for testing
  • T039-T040: HealthMonitor service with state tracking

Key Infrastructure Features Implemented

  • ModbusRelayController: Thread-safe Modbus TCP client with timeout handling
    • Uses Arc<Mutex<Context>> for concurrent access
    • Native Modbus TCP protocol (MBAP header, no CRC16)
    • Configurable timeout with tokio::time::timeout
  • MockRelayController: In-memory testing without hardware
    • Uses Arc<Mutex<HashMap<RelayId, RelayState>>> for state
    • Optional timeout simulation for error handling tests
  • SqliteRelayLabelRepository: Compile-time verified SQL queries
    • Automatic migrations via SQLx
    • In-memory mode for testing
  • HealthMonitor: State machine for health tracking
    • Healthy -> Degraded -> Unhealthy transitions
    • Recovery on successful operations

Planned - Phases 4-8

  • 📋 US1: Monitor & toggle relay states - MVP (Phase 4)
  • 📋 US2: Bulk relay controls (Phase 5)
  • 📋 US3: Health status display (Phase 6)
  • 📋 US4: Relay labeling (Phase 7)
  • 📋 Production deployment (Phase 8)

See tasks.org for detailed implementation roadmap.

Architecture

Current:

  • Backend: Rust 2024 with Poem web framework (hexagonal architecture)
  • Configuration: YAML-based with environment variable overrides
  • API: RESTful HTTP with OpenAPI documentation
  • CORS: Production-ready configurable middleware with security validation
  • Middleware Chain: Rate Limiting -> CORS -> Data injection
  • Modbus Integration: tokio-modbus for Modbus TCP communication
  • Persistence: SQLite for relay labels with compile-time SQL verification

Planned:

  • Frontend: Vue 3 with TypeScript
  • Deployment: Backend on Raspberry Pi, frontend on Cloudflare Pages
  • Access: Traefik reverse proxy with Authelia authentication

Quick Start

Prerequisites

  • Rust 1.83+ (edition 2024)
  • Just command runner

Development

# Run development server
just run

# Run tests
just test

# Run linter
just lint

# Format code
just format

# Watch mode with bacon
bacon          # clippy-all (default)
bacon test     # test watcher

Configuration

Edit backend/settings/base.yaml for Modbus device settings:

modbus:
  host: "192.168.0.200"
  port: 502
  slave_id: 0
  timeout_secs: 5

relay:
  label_max_length: 50

Override with environment variables:

APP__MODBUS__HOST=192.168.1.100 cargo run

CORS Configuration

Development Mode (frontend on localhost:5173):

# backend/settings/development.yaml
cors:
  allowed_origins:
    - "*"  # Permissive for local development
  allow_credentials: false  # MUST be false with wildcard
  max_age_secs: 3600

Production Mode (frontend on Cloudflare Pages):

# backend/settings/production.yaml
cors:
  allowed_origins:
    - "https://sta.yourdomain.com"  # Specific origin only
  allow_credentials: true  # Required for Authelia authentication
  max_age_secs: 3600

Security Notes:

  • Wildcard "*" origin is only allowed with allow_credentials: false
  • Production must use specific origins (e.g., https://sta.example.com)
  • Multiple origins are supported as a list
  • Credentials must be enabled for Authelia authentication to work

Hardcoded Security Defaults:

  • Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS (all required methods)
  • Headers: content-type, authorization (minimum for API + auth)

Fail-Safe Defaults:

  • allowed_origins: [] (restrictive - no origins allowed)
  • allow_credentials: false
  • max_age_secs: 3600 (1 hour)

See CORS Configuration Guide for complete documentation.

API Documentation

The server provides OpenAPI documentation via Swagger UI:

  • Swagger UI: http://localhost:3100/
  • OpenAPI Spec: http://localhost:3100/specs

Current Endpoints:

  • GET /api/health - Health check endpoint
  • GET /api/meta - Application metadata

Planned Endpoints (see spec):

  • GET /api/relays - List all relay states
  • POST /api/relays/{id}/toggle - Toggle relay state
  • POST /api/relays/all/on - Turn all relays on
  • POST /api/relays/all/off - Turn all relays off
  • PUT /api/relays/{id}/label - Set relay label

Project Structure

Monorepo Layout:

sta/                          # Repository root
├── backend/                  # Rust backend workspace member
│   ├── src/
│   │   ├── lib.rs           - Library entry point
│   │   ├── main.rs          - Binary entry point
│   │   ├── startup.rs       - Application builder and server config
│   │   ├── telemetry.rs     - Logging and tracing setup
│   │   │
│   │   ├── domain/          - Business logic layer (Phase 2)
│   │   │   ├── relay/       - Relay domain aggregate
│   │   │   │   ├── types/   - RelayId, RelayState, RelayLabel newtypes
│   │   │   │   ├── entity.rs    - Relay aggregate with state control
│   │   │   │   ├── controller.rs - RelayController trait & ControllerError
│   │   │   │   └── repository/   - RelayLabelRepository trait
│   │   │   ├── modbus.rs    - ModbusAddress type with conversion
│   │   │   └── health.rs    - HealthStatus state machine
│   │   │
│   │   ├── application/     - Use cases and orchestration (Phase 3)
│   │   │   └── health/      - Health monitoring service
│   │   │       └── health_monitor.rs - HealthMonitor with state tracking
│   │   │
│   │   ├── infrastructure/  - External integrations (Phase 3)
│   │   │   ├── modbus/      - Modbus TCP communication
│   │   │   │   ├── client.rs       - ModbusRelayController (real hardware)
│   │   │   │   ├── client_test.rs  - Hardware integration tests
│   │   │   │   └── mock_controller.rs - MockRelayController for testing
│   │   │   └── persistence/ - Database layer
│   │   │       ├── entities/       - Database record types
│   │   │       ├── sqlite_repository.rs - SqliteRelayLabelRepository
│   │   │       └── label_repository.rs  - MockRelayLabelRepository
│   │   │
│   │   ├── presentation/    - API layer (planned Phase 4)
│   │   ├── settings/        - Configuration module
│   │   │   ├── mod.rs       - Settings aggregation
│   │   │   └── cors.rs      - CORS configuration
│   │   ├── route/           - HTTP endpoint handlers
│   │   │   ├── health.rs    - Health check endpoints
│   │   │   └── meta.rs      - Application metadata
│   │   └── middleware/      - Custom middleware
│   │       └── rate_limit.rs
│   │
│   ├── settings/            - YAML configuration files
│   │   ├── base.yaml        - Base configuration
│   │   ├── development.yaml - Development overrides
│   │   └── production.yaml  - Production overrides
│   └── tests/               - Integration tests
│       └── cors_test.rs     - CORS integration tests
│
├── migrations/              - SQLx database migrations
├── src/                     # Frontend source (Vue/TypeScript)
│   └── api/                 - Type-safe API client
├── docs/                    # Project documentation
│   ├── cors-configuration.md    - CORS setup guide
│   ├── domain-layer.md          - Domain layer architecture
│   └── Modbus_POE_ETH_Relay.md  - Hardware documentation
├── specs/                   # Feature specifications
│   ├── constitution.md      - Architectural principles
│   └── 001-modbus-relay-control/
│       ├── spec.md          - Feature specification
│       ├── plan.md          - Implementation plan
│       ├── tasks.org        - Task breakdown (org-mode format)
│       ├── data-model.md    - Data model specification
│       ├── types-design.md  - Domain types design
│       ├── domain-layer-architecture.md - Domain layer docs
│       └── lessons-learned.md - Phase 2/3 insights
├── package.json             - Frontend dependencies
├── vite.config.ts          - Vite build configuration
└── justfile                 - Build commands

Technology Stack

Currently Used:

  • Rust 2024 edition
  • Poem 3.1 (web framework with OpenAPI support)
  • Tokio 1.48 (async runtime)
  • tokio-modbus (Modbus TCP client for relay hardware)
  • SQLx 0.8 (async SQLite with compile-time SQL verification)
  • async-trait (async methods in traits)
  • config (YAML configuration)
  • tracing + tracing-subscriber (structured logging)
  • governor (rate limiting)
  • thiserror (error handling)
  • serde + serde_yaml (configuration deserialization)

Frontend (scaffolding complete):

  • Vue 3 + TypeScript
  • Vite build tool
  • openapi-typescript (type-safe API client generation)

Testing Strategy

Phase 0.5 CORS Testing:

  • Unit Tests: 11 tests in backend/src/settings/cors.rs
    • CorsSettings deserialization (5 tests)
    • From for Cors trait (6 tests)
    • Security validation (wildcard + credentials check)
  • Integration Tests: 9 tests in backend/tests/cors_test.rs
    • Preflight OPTIONS requests
    • Actual request CORS headers
    • Max-age configuration
    • Credentials handling
    • Allowed methods verification
    • Wildcard origin behavior
    • Multiple origins support
    • Unauthorized origin rejection

Test Coverage Achieved: 15 comprehensive tests covering all CORS scenarios

Phase 2 Domain Layer Testing:

  • Unit Tests: 50+ tests embedded in domain modules
    • RelayId validation (5 tests)
    • RelayState serialization (3 tests)
    • RelayLabel validation (5 tests)
    • Relay aggregate behavior (8 tests)
    • ModbusAddress conversion (3 tests)
    • HealthStatus state transitions (15 tests)
  • TDD Approach: Red-Green-Refactor for all implementations
  • Coverage: 100% for domain layer (zero external dependencies)

Test Coverage Achieved: 100% domain layer coverage with comprehensive test suites

Phase 3 Infrastructure Testing:

  • MockRelayController Tests: 6 tests in mock_controller.rs
    • Read/write state operations
    • Read/write all relay states
    • Invalid relay ID handling
    • Thread-safe concurrent access
  • ModbusRelayController Tests: Hardware integration tests (#[ignore])
    • Real hardware communication tests
    • Connection timeout handling
  • SqliteRelayLabelRepository Tests: Database layer tests
    • CRUD operations on relay labels
    • In-memory database for fast tests
    • Compile-time SQL verification
  • HealthMonitor Tests: 15+ tests in health_monitor.rs
    • State transitions (Healthy -> Degraded -> Unhealthy)
    • Recovery from failure states
    • Concurrent access safety

Test Coverage Achieved: Comprehensive coverage across all layers with TDD approach

Documentation

Configuration Guides

Architecture Documentation

Development Guides

License

This project is under the AGPL-3.0 license. You can find it in the LICENSE.md file.

Description
Smart Temperature & Appliance control
Readme 1.1 MiB
Languages
Rust 95.4%
TypeScript 1.6%
Nix 1.4%
Vue 0.6%
CSS 0.5%
Other 0.5%