# Stats Administrator & Developer Guide

This guide is for administrators, power users, and developers who want to understand how **Stats** is built, how to deploy or build it, how it uses your system, and how to contribute. For day-to-day usage, see the companion **User Guide**.

Stats is a native macOS menu bar system monitor written in **Swift** for **AppKit (Cocoa)**. It has no server component and no external runtime dependencies beyond Apple's own frameworks. Its defining architectural trait is a **plugin-style module system**: a shared framework (`Kit`) defines the contracts, and each monitor (CPU, RAM, GPU, …) is an independent module/target that plugs into it.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Architecture Overview](#2-architecture-overview)
3. [Repository Structure](#3-repository-structure)
4. [The Kit Framework](#4-the-kit-framework)
5. [Module Lifecycle](#5-module-lifecycle)
6. [Data Persistence](#6-data-persistence)
7. [The SMC Subsystem & Privileged Helper](#7-the-smc-subsystem--privileged-helper)
8. [Remote / System Stats Integration](#8-remote--system-stats-integration)
9. [The Updater](#9-the-updater)
10. [Building From Source](#10-building-from-source)
11. [Configuration Reference](#11-configuration-reference)
12. [Security Analysis](#12-security-analysis)
13. [Code Patterns & Conventions](#13-code-patterns--conventions)
14. [Operations & Troubleshooting](#14-operations--troubleshooting)
15. [Potential Improvements](#15-potential-improvements)
16. [Contributing](#16-contributing)
17. [Quick Reference](#17-quick-reference)

---

## 1. Executive Summary

| Aspect | Detail |
|---|---|
| Purpose | macOS menu bar system monitor (CPU, GPU, RAM, Disk, Network, Battery, Sensors, Bluetooth, Clock, Remote) |
| Language | Swift 5 (some files Swift 6) |
| UI framework | AppKit / Cocoa (programmatic, no Storyboards for the modules) |
| Minimum OS | macOS 12 (Monterey) |
| Architectures | Apple Silicon (arm64) and Intel (x86_64) |
| Distribution | Signed `.dmg` from GitHub Releases; Homebrew cask `stats` |
| External services | `api.mac-stats.com`, `api.github.com`, `app.system-stats.com` (optional) |
| License | MIT |
| Privileged component | Optional SMC helper (XPC, `/Library/PrivilegedHelperTools`) for fan control |

The codebase is organized as an **Xcode workspace of many targets**: the main app (`Stats`), a shared framework (`Kit`), one framework target per monitor under `Modules/`, an SMC helper toolset (`SMC/`), a WidgetKit extension (`Widgets/`), and a `LaunchAtLogin` helper.

> [!NOTE]
> There is no package-manager manifest with third-party dependencies — Stats relies entirely on Apple frameworks (Cocoa, IOKit, CoreBluetooth, SystemConfiguration, Metal, ServiceManagement, UserNotifications, WidgetKit, WebKit, Carbon). This keeps the supply chain minimal.

---

## 2. Architecture Overview

```text
                         ┌───────────────────────────────────────────┐
                         │              Stats.app (main)               │
                         │  AppDelegate                                │
                         │   • holds `modules: [Module]`               │
                         │   • global key monitor, updater, support    │
                         │   • SettingsWindow / Setup / Update / Support│
                         │   • CombinedView (merged menu bar item)     │
                         └───────────────┬─────────────────────────────┘
                                         │ imports
        ┌────────────────────────────────┼─────────────────────────────────┐
        │                                 │                                  │
   ┌────▼─────┐   ┌──────────┐   ┌────────▼────────┐   ...   ┌─────────────┐
   │  CPU     │   │  RAM     │   │   Network        │         │  Remote     │
   │ (Module) │   │ (Module) │   │   (Module)       │         │ (Module)    │
   └────┬─────┘   └────┬─────┘   └────────┬─────────┘         └──────┬──────┘
        │ subclass of  │                  │                          │
        └──────────────┴───────┬──────────┴──────────────────────────┘
                               ▼
                   ┌──────────────────────────┐
                   │        Kit (framework)    │
                   │  Module / Reader / Widget │
                   │  Popup / Portal / Settings│
                   │  Store / DB / Notifications│
                   │  SMC client / SystemKit   │
                   └─────────────┬─────────────┘
                                 │ XPC (fan control only)
                                 ▼
                   ┌──────────────────────────┐
                   │  SMC Helper (privileged)  │
                   │  /Library/PrivilegedHelperTools │
                   └──────────────────────────┘
```

**Flow:** `AppDelegate` instantiates all modules into a global `modules` array, then calls `mount()` on each. A mounted module starts its **Readers** (background pollers), which read system data and fire callbacks. Each callback updates the module's **menu bar widgets**, its **pop-up**, its **portal** (the row shown in the combined pop-up), and its **notifications**. The `Kit` framework supplies all of these base classes; modules subclass and specialize them.

### Key design properties

- **Loose coupling via `NotificationCenter`.** Modules and views communicate through named notifications (`.toggleModule`, `.togglePopup`, `.toggleSettings`, `.moduleRearrange`, `.pause`, `.remoteState`, …) rather than direct references. This keeps modules independent.
- **Configuration as data.** Each module ships a `config.plist` (`Name`, `State`, `Symbol`, `Widgets`, `Settings`, `Preview`). The `module_c` struct parses it at init to decide the module's default state, default widget, and available widgets. Adding/removing a widget is a plist edit, not code.
- **Persistence is the source of truth.** All user choices live in `UserDefaults` via the `Store` wrapper; historical samples live in a small embedded `DB`. There is no in-memory-only state that matters across launches.

---

## 3. Repository Structure

```text
Stats/                 Main application target
  AppDelegate.swift      App entry, module registry, update/support scheduling
  helpers.swift          Argument parsing, version migration, dock/login setup
  Views/                 Settings UI
    Settings.swift         SettingsWindow (sidebar + toolbar), MainView, SidebarView
    AppSettings.swift      ApplicationSettings (global preferences)
    Dashboard.swift        Read-only device information panel
    CombinedView.swift     Merged single menu bar item + combined pop-up
  Supporting Files/      Info.plist, assets, .lproj localizations (40+ languages)

Kit/                   Shared framework (the plugin contract)
  constants.swift        Sizes, ModuleType enum, widget_t enum
  extensions.swift       UI helpers, color/format helpers, KeyboardShartcutView
  helpers.swift          LaunchAtLogin, Units/DiskSize, label/value views, rows
  module/                Module, Reader, Widget, Popup, Portal, notifications
  Widgets/               Widget implementations (Mini, LineChart, BarChart, …)
  plugins/               SMC client, SystemKit, sensor plugins
  Supporting Files/, lldb/

Modules/               One framework target per monitor
  CPU/ GPU/ RAM/ Disk/ Net/ Battery/ Sensors/ Bluetooth/ Clock/ Remote/
    main.swift             Module subclass + data structs + reader wiring
    readers.swift          Reader subclasses (the actual data collection)
    settings.swift         Per-module settings UI
    popup.swift            Per-module pop-up UI
    portal.swift           Combined-pop-up row
    notifications.swift    Threshold notifications
    widget.swift / preview.swift (where applicable)
    config.plist           Module manifest (name, widgets, settings, preview)

SMC/                   Command-line SMC tool (`smc`) + privileged Helper
  main.swift             CLI: list/set/fan/fans/reset/help
  Helper/                XPC privileged helper for fan control
Widgets/               WidgetKit extension (desktop / Notification Center)
LaunchAtLogin/         Tiny helper binary that relaunches the main app
Stats.xcodeproj/       Xcode project & shared schemes
Tests/                 Unit tests
```

> [!NOTE]
> The bulk of the file count is **localization** (`*.lproj` for 40+ languages) and **asset catalogs** (device icons). The hand-written Swift source is concentrated in `Kit/`, `Modules/*/`, and `Stats/`.

---

## 4. The Kit Framework

`Kit` is the contract every module obeys. Understanding these five abstractions explains the whole app.

### 4.1 `Module` (base class)

`Module` (in `Kit/module/module.swift`) is the superclass of every monitor. It owns:

- a `module_c` config (parsed from the target's `config.plist`),
- a `MenuBar` of `SWidget`s,
- an optional `Window` (settings), `PopupWindow`, `Portal`, `NotificationsWrapper`, and `PreviewWrapper`,
- a list of `Reader_p` pollers.

`module_c` reads the plist keys:

| Plist key | Meaning |
|---|---|
| `Name` | Module display name (required) |
| `State` | Default enabled state |
| `Symbol` / `AlternativeSymbol` | SF Symbol for the module icon |
| `Widgets` | Dictionary of available widgets, each with `Default` and `Order` |
| `Settings` | Settings metadata |
| `Preview` | Preview availability |

### 4.2 `Reader<T>` (data collection)

`Reader<T: Codable>` (in `Kit/module/reader.swift`) is a generic, thread-safe poller:

- Holds a typed `value: T?` behind a serial dispatch queue.
- Runs on a configurable interval (`{module}_updateInterval`, default 1s) via an internal `Repeater`, with an optional **second-boundary aligned** mode for clocks.
- Persists the last value to `DB` and forwards it to `SystemStats.shared.send(...)` for remote monitoring.
- Supports `start` / `pause` / `stop` / `lock` / `unlock` and a **sleep mode** so readers stop when the Mac sleeps.
- `popup`/`preview` readers run only while their UI is visible, saving energy.

Each module defines its own readers (e.g. CPU has `LoadReader`, `ProcessReader`, `TemperatureReader`, `FrequencyReader`/`LimitReader`, `AverageLoadReader`).

> [!NOTE]
> `LimitReader` (Intel) vs. `FrequencyReader` (Apple Silicon) are selected with `#if arch(x86_64)` / `#else`. This compile-time branching for architecture-specific metrics recurs throughout the modules.

### 4.3 `widget_t` (widget catalog)

The `widget_t` enum enumerates all widget styles. Each maps to a concrete `WidgetWrapper` subclass:

| `widget_t` | Raw value | Class |
|---|---|---|
| `mini` | `mini` | `Mini` |
| `lineChart` | `line_chart` | `LineChart` |
| `barChart` | `bar_chart` | `BarChart` |
| `pieChart` | `pie_chart` | `PieChart` |
| `networkChart` | `network_chart` | `NetworkChart` |
| `speed` | `speed` | `SpeedWidget` |
| `battery` | `battery` | `BatteryWidget` |
| `batteryDetails` | `battery_details` | `BatteryDetailsWidget` |
| `stack` | `sensors` | `StackWidget` |
| `memory` | `memory` | `MemoryWidget` |
| `label` | `label` | `Label` |
| `tachometer` | `tachometer` | `Tachometer` |
| `state` | `state` | `DotWidget` |
| `text` | `text` | `TextWidget` |

`widget_t.new(...)` instantiates both a live widget and a preview image. A module's data callback then `switch`es on the concrete widget type to push values — this switch is the authoritative list of which widgets a module actually feeds.

### 4.4 `Popup`, `Portal`, `Settings`

- **`Popup_p` / `PopupWrapper`** — the detail panel. A `PopupWindow` (borderless `NSPanel`) hosts a `PopupView` with a header (`HeaderView`) offering Activity Monitor/Calendar/System Stats shortcuts and a settings button. Popups support a per-module **keyboard shortcut** (stored as `{module}_popup_keyboardShortcut`).
- **`Portal_p` / `PortalWrapper`** — a compact module summary row shown in the **combined** pop-up.
- **`Settings_v`** — per-module settings view, wired to the module via callbacks (`callback`, `setInterval`, `setTopInterval`, selection handlers, etc.).

### 4.5 Helpers

`Kit` also provides reusable UI builders (`popupRow`, `portalRow`, `separatorView`, `switchView`, `selectView`, `sliderView`, `colorSelectView`), formatting types (`Units`, `DiskSize`), a `KeyboardShartcutView` recorder, and `LaunchAtLogin` (using `SMAppService` on macOS 13+ and the legacy `SMLoginItemSetEnabled` below that, with a `migrate()` path).

---

## 5. Module Lifecycle

```text
init()        Parse config.plist → build menuBar widgets, popup, settings,
              portal, notifications. Read enabled state from Store.
              isAvailable() decides if the module can run at all.
   │
mount()       Called by AppDelegate at startup (in reverse order). If enabled,
              start all readers and enable the menu bar item.
   │
enable()      Persist enabled=true, start readers, show widgets.
disable()     Persist enabled=false, stop readers, hide widgets & popup.
   │
terminate()   willTerminate() hook, stop+terminate readers, disable menu bar.
```

`isAvailable()` is overridden per module — e.g. **Battery** checks `IOPSCopyPowerSourcesList`, **Network** checks for any `SCNetworkInterface`. Unavailable modules are forced off.

A module is shown in the menu bar only when **enabled and has at least one active widget**; `AppDelegate.showSettingsIfNoActiveWidgets()` opens Settings on launch when nothing is active.

**Pause** is global: posting `.pause` disables every enabled module and swaps the menu bar for a single app icon, restoring state when unpaused.

---

## 6. Data Persistence

| Store | Backing | Holds |
|---|---|---|
| `Store.shared` | `UserDefaults` | All user preferences (module states, colors, intervals, widget choices, thresholds, clocks, etc.) |
| `DB.shared` | Embedded key/value DB | Last value per `Reader` (`{Module}@{ReaderName}`), optional time-series history |
| Widget defaults | App-group `UserDefaults` (`{TeamId}.eu.exelban.Stats.widgets`) | Data shared with the WidgetKit extension |

- Preference keys follow `{ModuleName}_{option}` (e.g. `CPU_splitValue`, `Disk_<uuid>_popup`, `Network_publicIPRefreshInterval`).
- `Store` supports `export(to:)` / `import(from:)` (the `Stats.plist` backup) and `reset()`.
- Readers throttle DB writes (roughly every `interval × 10` seconds) to limit disk churn.
- On version change, `parseVersion()` migrates or, if no prior version is recorded, resets the store.

> [!TIP]
> The app-group suite name embeds the developer **Team ID** (`{TeamId}.eu.exelban.Stats.widgets`). If you re-sign Stats under a different team for a custom build, the WidgetKit extension must use the matching app group or widgets won't receive data.

---

## 7. The SMC Subsystem & Privileged Helper

Stats reads hardware sensors (temperatures, voltages, power, fans) through the **System Management Controller (SMC)**. Reading is unprivileged; **changing fan speed/mode is privileged** and goes through a separate helper.

### 7.1 The `smc` CLI tool

`SMC/main.swift` builds a small command-line tool bundled with the app:

```text
./smc [command]

Commands:
  list     list keys and values
  set      set value to a key            (-k <4-char key> -v <int>)
  fan      set fan speed / mode          (<id> -v <speed> | -m <0|1>)
  fans     list fans (count, actual/min/max/target speed, mode)
  reset    reset Ftst — Apple Silicon only
  help     help menu

Flags (for list):
  -t  temperature sensors    -v  voltage sensors
  -p  power sensors          -f  fans
```

Fan mode `0` = automatic, `1` = manual. SMC keys are 4 characters (e.g. `FNum`, `F0Ac`, `F0Mx`).

### 7.2 The privileged Helper (fan control)

Fan **writes** are performed by an XPC helper installed at `/Library/PrivilegedHelperTools/eu.exelban.Stats.SMC.Helper`, registered as a launch daemon (`/Library/LaunchDaemons/eu.exelban.Stats.SMC.Helper.plist`).

```text
Stats.app  ──XPC──►  SMC.Helper  ──shells──►  smc fan <id> -m/-v ...
                       │
                       └─ validates caller code signature on every connection
```

Security-relevant behavior in `SMC/Helper/main.swift`:

- The helper listens on a Mach service (`NSXPCListener(machServiceName: "eu.exelban.Stats.SMC.Helper")`).
- **Every incoming XPC connection is validated** with `CodesignCheck.codeSigningMatches(pid:)` — the caller's signing certificates must match the helper's own. Mismatched callers are rejected. This prevents arbitrary processes from driving fan control.
- The helper exposes only `setFanMode`, `setFanSpeed`, `resetFanControl`, `version`, and `uninstall` over the `HelperProtocol`.
- It self-terminates when the last connection drops, and `uninstall` unloads the daemon via `launchctl` and removes both the daemon plist and the helper binary.

> [!WARNING]
> Fan control is in **legacy maintenance mode** — no new features or fixes. The privileged helper is the highest-risk component in the app's threat model; the code-signature check on each connection is the primary mitigation. Review it carefully before shipping a re-signed build.

To remove the helper, the app calls `SMCHelper.shared.uninstall()` (surfaced as **Uninstall fan helper** in Settings).

---

## 8. Remote / System Stats Integration

The **Remote** module and `SystemStats` client connect Stats to the optional **System Stats** cloud service.

| Endpoint base | Constant | Purpose |
|---|---|---|
| API host | `SystemStats.host` | REST + SSE data |
| App host | `SystemStats.appHost` = `app.system-stats.com` | Web links for machines/hosts |

Key behaviors:

- **Auth:** OAuth-style device flow (`access_token`/`refresh_token`/`device_code`/`user_code`/`verification_uri_complete`). Requests attach `Authorization: Bearer <accessToken>`.
- **Outbound monitoring:** when **Monitoring** is enabled, each `Reader` calls `SystemStats.shared.send(key:value:)`, serializing a compact `remote()` representation per data type (`RemoteType`).
- **Inbound dashboard:** the Remote module fetches `/remote/machine`, `/host`, `/group`, `/account` and renders machines/hosts grouped per the account's order. A **State** dot aggregates overall up/down/mixed status.
- **Live updates:** `RemoteMachineStream` opens an **SSE** connection (`/remote/machine/<id>/sse`) with exponential-backoff reconnect (capped at 60s) and parses `data:` frames into `RemoteUpdate`s.
- **Control:** disabled by default and gated behind an explicit warning dialog; enabling it allows the service to issue control actions.

> [!CAUTION]
> All Remote functionality is **opt-in** and requires login. The **Control** capability widens the trust boundary (the cloud can act on the machine); document and default it off in any managed deployment.

---

## 9. The Updater

Stats self-updates via `Updater(github: "exelban/stats", url: "https://api.mac-stats.com/release/latest")`.

- **Check cadence** is the `update-interval` preference: `silent` (background install), `atStart`, `oncePerDay`, `oncePerWeek`, `oncePerMonth` (`AppUpdateInterval`). Background activity is scheduled with `NSBackgroundActivityScheduler`.
- On a new version it either installs silently, posts a notification (clicking it downloads+installs), or shows the update window, depending on notification authorization.
- After installing, the new app is relaunched and the updater cleans up the temporary DMG mount and downloaded `.dmg` via the `--mount-path` / `--dmg-path` arguments (both are path-validated to temp/Downloads to avoid deleting arbitrary files).
- `api.github.com` is the fallback update source.

> [!NOTE]
> The public-IP lookup also uses `api.mac-stats.com`. The maintainer runs this server specifically to avoid third-party IP providers; both update and IP requests can be blocked with a network filter at the cost of those features.

---

## 10. Building From Source

### Prerequisites

- macOS with **Xcode** (recent enough to target macOS 12+, Swift 5/6).
- The repository: `git clone https://github.com/exelban/stats.git`.
- For full builds: an Apple **Developer Team ID** for code signing (required for the app group, WidgetKit extension, and the privileged helper).

### Build steps

```bash
git clone https://github.com/exelban/stats.git
cd stats
open Stats.xcodeproj
```

1. Open `Stats.xcodeproj` in Xcode.
2. Select the **Stats** scheme.
3. Set your **Team** in *Signing & Capabilities* for each signed target (app, widgets extension, helper).
4. **Product → Run** (⌘R) to build and launch, or **Product → Archive** to produce a distributable build.

> [!IMPORTANT]
> Several capabilities depend on correct signing: the **app group** (`{TeamId}.eu.exelban.Stats.widgets`) that feeds macOS widgets, and the **privileged helper** (which validates that callers share its signing identity). Building with a personal team works for local runs, but widgets and fan control require the entitlements/app group to line up.

### Code style

The repo uses **SwiftLint** (`.swiftlint.yml`). Notable limits: `line_length: 200`, `file_length` warning at 1400 / error at 1800, `type_body_length` 700/1000. Several rules are disabled or opt-in; many data-model type names are excluded from `identifier_name`.

> [!TIP]
> Run `swiftlint` before submitting a PR. The config disables `force_cast`, `cyclomatic_complexity`, and a few others as "todo", so don't be surprised by existing force-casts in older code.

---

## 11. Configuration Reference

All configuration is `UserDefaults` (via `Store`). There is **no config file** for end users — preferences are set in the UI and can be exported to `Stats.plist`.

### Global keys

| Key | Default | Values |
|---|---|---|
| `update-interval` | `silent` | `silent`, `atStart`, `oncePerDay`, `oncePerWeek`, `oncePerMonth` |
| `temperature_units` | `system` | `system`, plus Celsius/Fahrenheit options |
| `dockIcon` | `false` | bool |
| `keep_menubar_positions` | `false` | bool |
| `CombinedModules` | `false` | bool |
| `CombinedModules_spacing` | `none` | spacing option |
| `CombinedModules_separator` | `false` | bool |
| `CombinedModules_popup` | `true` | bool |
| `version` | — | last-run version (migration) |
| `pause` | `false` | global pause |
| `support_ts` | — | last support-window timestamp |

The macOS-widgets switch (`systemWidgetsUpdates_state`) lives in the **app-group** suite, not the main store.

### Representative per-module keys

| Pattern | Example | Meaning |
|---|---|---|
| `{Module}_state` | `CPU_state` | Module enabled |
| `{Module}_updateInterval` | `RAM_updateInterval` | Reader interval (sec) |
| `{Module}_popup_keyboardShortcut` | `CPU_popup_keyboardShortcut` | Pop-up hotkey |
| `{Module}_position` | `GPU_position` | Order in combined item |
| CPU | `CPU_usagePerCore`, `CPU_splitValue`, `CPU_clustersGroup`, `CPU_systemColor`, `CPU_userColor`, `CPU_eCoresColor`, `CPU_pCoresColor`, `CPU_sCoresColor` | CPU display options |
| RAM | `RAM_splitValue`, `RAM_appColor`, `RAM_wiredColor`, `RAM_compressedColor`, `RAM_textWidgetValue` | RAM display options |
| Disk | `Disk_disk`, `Disk_mainColor`, `Disk_textWidgetValue`, `Disk_<uuid>_popup` | Disk selection & display |
| GPU | `GPU_gpu`, `GPU_showType` | GPU selection & label |
| Network | `Network_publicIPRefreshInterval`, `Network_usageReset`, `Network_widgetActivationThreshold(State/Size)`, `Network_textWidgetValue` | Network behavior |
| Sensors | `Sensors_sensor`, `Sensors_fanValue` | Selected sensor / fan unit |
| Bluetooth | `ble_<id>`, `ble_<id>_notification` | Per-device visibility/threshold |
| Clock | `Clock_list`, `clock_<id>_popupState`, `clock_<id>_popupIndex` | Clock definitions |
| Remote | `Remote_machine_<id>`, `Remote_host_<id>` | Per-machine/host visibility |

---

## 12. Security Analysis

| Area | Assessment |
|---|---|
| **Privilege boundary** | Fan control runs in a separate root helper. Every XPC connection is gated by `CodesignCheck` (caller's signing certs must equal the helper's). This is the central security control — preserve it in forks. |
| **Code execution** | The helper shells out to the bundled `smc` tool (`/bin/sh -c "<smc> fan <id> ..."`). Inputs are integers from internal calls, not user free-text, limiting injection surface. |
| **Network** | Outbound only. Update checks (`api.mac-stats.com`, fallback `api.github.com`), public IP (`api.mac-stats.com`), and — only if logged in — System Stats (`app.system-stats.com`). Remote requests use `Authorization: Bearer` over HTTPS. |
| **Auth tokens** | Remote uses OAuth device flow; `access_token`/`refresh_token` are held by the `SystemStats.auth` client. Treat the token store as sensitive. |
| **Data exfiltration** | Monitoring data leaves the machine only when the user enables **Monitoring** in System Stats. **Control** must be explicitly enabled behind a warning. |
| **Argument handling** | `--mount-path` / `--dmg-path` are validated to temp/Downloads and reject `..` traversal before deleting anything. |
| **App Transport Security** | Community issue [#3320](https://github.com/exelban/stats/issues/3320) requested hardening ATS (avoiding a global `NSAllowsArbitraryLoads`). Verify the shipped `Info.plist` ATS posture when auditing. |

> [!CAUTION]
> For managed/enterprise deployments: keep **Control** disabled, consider blocking `api.mac-stats.com` if you don't want update/IP calls, and audit the privileged helper's signing identity. The helper is installed system-wide (`/Library/...`) and persists until explicitly uninstalled.

---

## 13. Code Patterns & Conventions

- **Programmatic AppKit.** Views are built in code (`NSStackView`, `NSGridView`, custom `NSView` subclasses). No SwiftUI in the core app (SwiftUI/WidgetKit only in the widget extension).
- **Notification-driven coordination.** Cross-component events flow through `NotificationCenter` with `Notification.Name` extensions (`.toggleModule`, `.togglePopup`, `.toggleSettings`, `.pause`, `.moduleRearrange`, `.remoteState`, etc.).
- **Thread-safe value holders.** Shared mutable collections (e.g. `Disks`, `GPUs`, `Reader.value`) are guarded by dedicated serial `DispatchQueue`s with `sync` get/set.
- **Codable everywhere.** Data models conform to `Codable` for DB persistence and remote serialization; many implement a compact `remote()` string for the wire format.
- **Architecture branching.** `#if arch(x86_64)` / `#if arch(arm64)` selects Intel vs. Apple Silicon code paths (frequency vs. scheduler limits, fan reset).
- **Config-as-plist.** Module capabilities (widgets, default state) are declared in `config.plist`, parsed by `module_c`.
- **Localization-first.** All user-facing strings pass through `localizedString(...)`; 40+ `.lproj` bundles are maintained by the community.

---

## 14. Operations & Troubleshooting

### Where things live at runtime

| Item | Location |
|---|---|
| Preferences | `UserDefaults` for `eu.exelban.Stats` |
| Widget data | App group `{TeamId}.eu.exelban.Stats.widgets` |
| Privileged helper | `/Library/PrivilegedHelperTools/eu.exelban.Stats.SMC.Helper` |
| Helper launch daemon | `/Library/LaunchDaemons/eu.exelban.Stats.SMC.Helper.plist` |
| Login item | Registered via `SMAppService.mainApp` (macOS 13+) |

### Common operational tasks

```bash
# Reset all settings (clean slate)
/Applications/Stats.app/Contents/MacOS/Stats --reset

# Launch with certain modules disabled
/Applications/Stats.app/Contents/MacOS/Stats --disable sensors,bluetooth

# Remove the privileged fan helper
sudo launchctl unload /Library/LaunchDaemons/eu.exelban.Stats.SMC.Helper.plist
sudo rm /Library/LaunchDaemons/eu.exelban.Stats.SMC.Helper.plist
sudo rm /Library/PrivilegedHelperTools/eu.exelban.Stats.SMC.Helper
```

> [!TIP]
> Prefer the in-app **Uninstall fan helper** button over manual `rm`; it performs the same `launchctl` unload and file removal in the correct order.

### Diagnostics

| Symptom | Check |
|---|---|
| Menu bar icons missing (macOS 26) | **System Settings → Menu Bar → Stats = ON** |
| Desktop widget empty | **macOS widgets** setting enabled; app group/Team ID match |
| High CPU | Disable Sensors/Bluetooth; raise update intervals |
| Network falsely DOWN | Restart Stats; update to latest |
| Fan helper install fails | Approve admin prompt; uninstall stale helper, retry |
| Update breaks app (Gatekeeper) | Reinstall from `Stats.dmg` |

---

## 15. Potential Improvements

These are drawn from the public issue tracker and visible code structure — candidates for contribution.

- **Fan control modernization.** It's explicitly legacy. A maintained, well-tested fan-control path (and clearer install UX, given issues [#3330](https://github.com/exelban/stats/issues/3330)/[#3331](https://github.com/exelban/stats/issues/3331)/[#3332](https://github.com/exelban/stats/issues/3332)) would close a recurring pain point.
- **ATS hardening** ([#3320](https://github.com/exelban/stats/issues/3320)) — avoid any global arbitrary-loads allowance.
- **Network interface status robustness** ([#2545](https://github.com/exelban/stats/issues/2545), [#3293](https://github.com/exelban/stats/issues/3293)) — eliminate false "DOWN" without requiring a restart.
- **Apple Silicon sensor mapping** — sensor keys change per SoC; a maintained mapping (and handling for the M5 "super cores" frequency gap, [#3325](https://github.com/exelban/stats/issues/3325)) would improve accuracy.
- **Per-monitor multi-display windows** and **richer RAM data** ([#3327](https://github.com/exelban/stats/issues/3327)).
- **CPU core frequencies on the menu bar** ([#3333](https://github.com/exelban/stats/issues/3333)) and **usage history in an expanded window** ([#1194](https://github.com/exelban/stats/issues/1194)).
- **Fan-read debounce on startup** to avoid spurious 100% notifications ([#3211](https://github.com/exelban/stats/issues/3211)).
- **Code-health items the lint config flags as "todo":** re-enable `cyclomatic_complexity`, `type_name`, and reduce `force_cast` usage.

---

## 16. Contributing

The project has a deliberate workflow (from the repository README):

1. **Open an issue first** to discuss the feature or bug. PRs are expected to map to a discussed issue, or they may be closed automatically.
2. **Exceptions** that can skip discussion: localization/translation changes, and contributions from established contributors whose work aligns with the project.
3. **Translations** are community-driven — add a new `.lproj` or improve an existing one.
4. Follow the existing **SwiftLint** configuration and the programmatic-AppKit, notification-driven, `Codable`-model conventions.

To add a **new module**: create a target under `Modules/`, subclass `Module`, add a `config.plist` declaring its widgets/settings, implement one or more `Reader`s plus the `popup`/`settings`/`portal`/`notifications` views, and register an instance in the `modules` array in `Stats/AppDelegate.swift`.

To add a **new widget**: add a case to `widget_t`, implement a `WidgetWrapper` subclass, wire it in `widget_t.new(...)`, and handle it in each relevant module's data callback.

---

## 17. Quick Reference

### Targets

| Target | Role |
|---|---|
| `Stats` | Main menu bar app |
| `Kit` | Shared framework (Module/Reader/Widget/Popup/Portal/Settings/Store/DB/SMC client) |
| `Modules/*` | One framework per monitor (CPU, GPU, RAM, Disk, Net, Battery, Sensors, Bluetooth, Clock, Remote) |
| `SMC` | `smc` CLI tool |
| `SMC/Helper` | Privileged XPC fan-control helper |
| `Widgets` | WidgetKit extension |
| `LaunchAtLogin` | Relaunch helper |

### `smc` commands

| Command | Purpose |
|---|---|
| `list [-t/-v/-p/-f]` | List keys/values (filterable) |
| `set -k <key> -v <int>` | Write a 4-char SMC key |
| `fan <id> -v <speed>` / `-m <0\|1>` | Set fan speed / mode |
| `fans` | List fans and their speeds/mode |
| `reset` | Reset fan control (Apple Silicon) |
| `help` | Usage |

### Launch arguments

| Argument | Effect |
|---|---|
| `--reset` | Reset all settings |
| `--disable a,b` | Disable named modules |
| `--mount-path <p>` | (updater) unmount temp DMG |
| `--dmg-path <p>` | (updater) delete downloaded DMG |

### External hosts

| Host | Purpose |
|---|---|
| `api.mac-stats.com` | Updates + public IP |
| `api.github.com` | Fallback update check |
| `app.system-stats.com` | Remote dashboard (opt-in) |

### Key notifications

`.toggleModule`, `.togglePopup`, `.toggleSettings`, `.openModuleSettings`, `.togglePreview`, `.pause`, `.moduleRearrange`, `.toggleOneView`, `.combinedModulesPopup`, `.remoteState`, `.remoteAuthenticated`, `.fanHelperState`, `.refreshPublicIP`, `.resetTotalNetworkUsage`, `.clickInSettings`.

---

_Generated June 2026 from exelban/stats at 89ac79e. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
