Tauri-independent core crate for HardwareVisualizer.
hardviz-core (library name hardviz_core) owns sensor collection, persistence,
the in-process event bus, and Core-consumed settings. It is the lower half of
the Core / App split introduced by #1402: everything that does
not need a Tauri context lives here, and the Tauri app crate
(src-tauri/) depends on it via a path dependency.
- Sample CPU / memory / GPU / process metrics on a periodic loop.
- Hold sensor history (ring buffers) behind a Core-owned read API.
- Publish each tick as a
MetricsSnapshoton a single in-process broadcast bus that any number of subscribers (window, future tray / overlay / alerts) can fan out from. - Run Core-owned persistence workers for hardware archive and storage SMART snapshots.
- Run a SQLite schema-version preflight check before the App brings up the Tauri SQL plugin.
- Hold the subset of on-disk settings whose values change Core behavior.
- Expose the platform abstraction (Windows / Linux / macOS) and the infrastructure-level providers (sysinfo, NVAPI, WMI, procfs, …) used by it.
These rules are enforced by structure, not by review:
- No
tauridependency.core/Cargo.tomldoes not listtauri, so anyuse tauri::*;undercore/src/simply fails to compile. The dependency graph is the boundary. - No
window.emit(...)in Core. Core publishesMetricsSnapshottoEventBus. Translating a snapshot into a Tauri event is an App-side concern (src-tauri/src/adapters/window.rs). - Persistence does not share state with the collector. Archive persistence
subscribes to
EventBusrather than reaching into the collector's history bag, so the two run as independent tasks. - Settings split by consumer.
CoreSettingsdeserializes only the keys that affect Core (currentlyhardwareArchive). UI-only keys (theme,language,lineGraph*,temperatureUnit, …) stay App-side. Both crates read and write the same JSON file;CoreSettings::save_to_pathmerges into the existing object so App-owned keys survive. - Presentation lives in App. Core stores temperatures in °C; conversion to °F is done in the App-side adapter, not in the collector.
core/src/
├── lib.rs
├── event_bus.rs ← tokio broadcast<MetricsSnapshot> fan-out
├── collector/ ← sampling loop + history ring buffers
│ ├── history.rs HistoryStore (Arc<Mutex<...>> behind a read API)
│ ├── sampling.rs sample_system / sample_gpu cycle
│ └── system_monitor.rs SystemMonitorController (drives the tokio task)
├── persistence/ ← Core-owned persistence workers and primitives
│ ├── archive.rs Hardware archive writer and cleanup
│ ├── archive_data.rs Archive row mapping helpers
│ ├── preflight.rs DB schema-version compatibility check
│ └── storage_smart.rs Storage SMART snapshot worker
├── settings/ ← Core-consumed settings (subset of settings.json)
│ ├── mod.rs CoreSettings (load / save with App-key merge)
│ ├── hardware_archive.rs HardwareArchiveSettings
│ └── storage_smart.rs Storage SMART settings and identity key
├── platform/ ← Cross-platform hardware access
│ ├── traits.rs Memory / GPU / Network / Motherboard traits
│ ├── factory.rs PlatformFactory (compile-time OS selection)
│ ├── windows/ linux/ macos/
├── infrastructure/ ← External I/O backing the platform layer
│ ├── database/ SQLite pool + archive / SMART writers
│ └── providers/ sysinfo / NVAPI / WMI / procfs / DRM / …
├── monitoring/ ← Monitoring state types
├── models/ ← Shared data types (MetricsSnapshot, GpuMetric, …)
├── enums/ ← Cross-cutting enums (errors, hardware, settings)
└── utils/ ← Logger macros, formatters, IP / rounding helpers
Core owns persistence workers that do not need Tauri objects. The App crate still owns Tauri-specific startup decisions: resolving the SQLite path, registering Tauri SQL migrations, and deciding whether DB-dependent workers can start after preflight.
core/src/infrastructure/providers/ contains lower-level data access helpers
used by the platform layer and Core services.
Cross-platform providers:
sysinfo_provider.rsreads CPU and storage facts throughsysinfo.smartctl.rswraps the externalsmartctlcommand and parses SMART data.
Windows providers:
windows/wmi_provider.rsreads Windows WMI data for memory, motherboard, and network-related facts.windows/nvapi_provider.rsreads NVIDIA GPU data through NVAPI.windows/adl_provider.rsreads AMD GPU sensors through AMD ADL.windows/pdh_provider.rsreads GPU engine utilization through PDH.windows/directx.rsreads DXGI adapter data.windows/setupdi_provider.rsenumerates display adapters and PCI BDF addresses through SetupDi.windows/smart.rsreads SMART data through Windows WMI withsmartctlfallback.
Linux providers:
linux/procfs.rsreads procfs data such as memory facts.linux/net_sys.rsreads network interface facts from Linux system files and commands.linux/drm_sys.rs,linux/hwmon.rs,linux/kernel.rs, andlinux/lspci.rsread GPU-related data from DRM/sysfs/debugfs/lspci sources.linux/dmidecode.rsreads DMI hardware facts throughdmidecode.linux/smart.rsreads SMART data throughsmartctl, including Linux device candidate fallback.
macOS providers:
macos/sysctl.rsreads low-level values throughsysctlbyname.macos/system_profiler_displays.rsandmacos/system_profiler_memory.rsparsesystem_profilerJSON output.macos/gpu.rsandmacos/gpu_info.rsread GPU usage and adapter facts.macos/net_sys.rsreads network interface facts from macOS OS APIs.macos/smart.rsreads SMART data throughsmartctl, including diskutil candidate fallback.
Provider modules should stay below Core. App code should call Core APIs or App services instead of reaching into providers directly.
hardviz-core is a workspace member. From the repository root:
# Build only the core crate
cargo build -p hardviz-core
# Run Core tests without spinning up a Tauri runtime
cargo test -p hardviz-coreCore is also covered by the workspace-wide cargo tauri-fmt /
cargo tauri-lint / cargo tauri-test aliases defined in
.cargo/config.toml at the repository root.
hardviz-core (this crate)
│ ├─ publishes MetricsSnapshot on EventBus
│ ├─ exposes HistoryStore read API
│ └─ exposes SystemMonitorController
▼
src-tauri/ (App)
├─ adapters::window ─ subscribes to EventBus, emits HardwareMonitorUpdate
├─ adapters::tray ─ subscribes to EventBus, updates tray widget output
├─ commands::* ─ thin delegation to Core API + Tauri input/output
├─ app::startup ─ wires Core setup + DB preflight error dialog flow
└─ workers::* ─ owns Core controller / adapter handles for shutdown
- Epic: #1402 — split backend into Tauri-independent Core and thin App adapters
- Architecture:
docs/architecture/backend.md - App crate:
src-tauri/README.md