systant/CLAUDE.md
ryan ae778ebdab Initial systant implementation in Bun/TypeScript
A lightweight system monitoring agent that:
- Collects metrics via configurable shell commands
- Publishes to MQTT with Home Assistant auto-discovery
- Supports entity types: sensor, binary_sensor, light, switch, button
- Responds to commands over MQTT for controllable entities

Architecture:
- src/config.ts: TOML config loading and validation
- src/mqtt.ts: MQTT client with HA discovery
- src/entities.ts: Entity state polling and command handling
- index.ts: CLI entry point (run, check, once commands)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 19:52:47 -08:00

121 lines
3.7 KiB
Markdown

# Systant
A system monitoring agent that collects metrics, monitors services, and reports to MQTT/Home Assistant; and responds to events over MQTT to trigger commands or other behavior.
## Project Overview
Systant is a lightweight CLI tool written in Bun/TypeScript that:
- Collects system metrics (CPU, memory, disk, network)
- Monitors service health
- Publishes data to MQTT brokers
- Supports Home Assistant auto-discovery
- **Listens for MQTT commands** to trigger actions (run scripts, restart services, etc.)
- **Responds to events** with configurable handlers
- Runs as a daemon or one-shot command
### Architecture
```
index.ts # CLI entry point (yargs)
src/
commands/ # CLI command handlers
metrics/ # System metric collectors
mqtt/ # MQTT client and publishing
events/ # MQTT event listeners and handlers
actions/ # Executable actions (shell, service, notify)
ha/ # Home Assistant discovery
config/ # Configuration loading
```
### Event/Command System
Systant subscribes to MQTT topics and executes configured actions:
```
Topic: systant/{hostname}/command/{action}
Payload: { "args": [...], "timeout": 30 }
Topic: systant/{hostname}/event/{event_name}
Payload: { ... event data ... }
```
Actions are sandboxed and configurable via allowlists in the config file. Security is critical - never execute arbitrary commands without validation.
### Key Design Decisions
- **Single binary**: Compiles to standalone executable via `bun build --compile`
- **No external services**: Uses Bun built-ins (sqlite, file, etc.)
- **Config-driven**: TOML configuration for flexibility
- **Typed throughout**: Full TypeScript with strict mode
## Tech Stack
- **Runtime**: Bun (not Node.js)
- **CLI**: yargs
- **Config**: TOML
- **MQTT**: mqtt.js or Bun-native when available
- **Package**: Nix flake for reproducible builds
## Bun Conventions
Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>`
- Use `bunx <package> <command>` instead of `npx <package> <command>`
- Bun automatically loads .env, so don't use dotenv.
### Bun APIs
- `Bun.serve()` for HTTP/WebSocket servers
- `bun:sqlite` for SQLite (not better-sqlite3)
- `Bun.file()` for file I/O (not node:fs readFile/writeFile)
- `Bun.$\`cmd\`` for shell commands (not execa)
- Native `WebSocket` (not ws)
### Testing
```ts
import { test, expect, describe, beforeEach } from "bun:test";
describe("MetricCollector", () => {
test("collects CPU metrics", async () => {
const metrics = await collectCPU();
expect(metrics.usage).toBeGreaterThanOrEqual(0);
});
});
```
Run tests: `bun test`
Run specific: `bun test src/metrics`
Watch mode: `bun test --watch`
## Code Style
- Prefer `async/await` over callbacks
- Use explicit return types on public functions
- Prefer `interface` over `type` for object shapes
- Use `const` by default, `let` only when reassignment needed
- No classes unless state encapsulation is genuinely needed
- Prefer pure functions and composition
## Commands
```bash
bun run start # Run in development
bun run dist # Build standalone binary
bun test # Run tests
bun test --watch # Watch mode
```
## Planning Protocol
When implementing features:
1. Discuss the approach before writing code
2. Start with types/interfaces
3. Write tests alongside implementation
4. Keep PRs focused and small