add readme and AGENTS slop

This commit is contained in:
2026-02-21 16:43:55 +01:00
parent 8868eb8736
commit 30e15d8f95
2 changed files with 449 additions and 99 deletions

366
README.md
View File

@@ -5,137 +5,305 @@ A NixOS module for monitoring website content and command output changes.
## Overview
Panoptikon is a generic command output and website watcher that periodically runs scripts and reports changes. It's designed to be flexible and can monitor anything from API endpoints to system metrics.
Panoptikon is a flexible, secure, and modular system for monitoring changes to websites, API endpoints, and command outputs. It runs scripts at configurable intervals, detects changes by comparing outputs, and reports differences to various destinations.
**Perfect for:**
- Monitoring service status pages (GitHub, cloud providers)
- Tracking website content changes (blogs, news)
- Watching API endpoints (cryptocurrency prices, weather)
- System metrics monitoring (disk space, load, processes)
- Security canaries and breach detection
## Features
- **Flexible Watchers**: Monitor any command output or website content
- **Custom Frequencies**: Run scripts at any interval using systemd.timer syntax
- **Multiple Reporters**: Report changes to various destinations (IRC, Telegram, Prometheus, etc.)
- **Secret Support**: Securely pass credentials to scripts without exposing them in the Nix store
- **Stateful Tracking**: Automatically tracks previous output and reports only changes
- **Modular Design**: Easy to extend with custom watchers and reporters
- **Flexible Watchers**: Execute any script or command; monitor HTTP endpoints with built-in helpers
- **Custom Frequencies**: Use systemd timer syntax for any schedule (*/5 minutes, daily, weekly, etc.)
- **Multiple Reporters**: Notify via IRC, Telegram, Matrix, email, desktop notifications, wall, or custom scripts
- **Secret Support**: Securely pass credentials using `LoadCredential=` without exposing them in the Nix store
- **Stateful Tracking**: Automatic diffing; only reports actual changes
- **Modular Design**: Built-in helpers for HTML, JSON, and plain text; easy to create custom ones
- **Security Hardened**: Dedicated system user, filesystem isolation, and process protection
- **systemd Native**: Full integration with systemd for reliable scheduling and logging
## Installation
## Quick Start
Add Panoptikon to your NixOS configuration:
1. **Enable Panoptikon** in your NixOS configuration:
```nix
{ config, pkgs, ... }:
{
# Enable Panoptikon service
services.panoptikon.enable = true;
# Configure your watchers
services.panoptikon.watchers = {
# Your watcher configurations go here
};
}
```
## Configuration
### Basic Watcher Configuration
2. **Add a simple watcher**:
```nix
{
services.panoptikon.enable = true;
services.panoptikon.watchers = {
# Monitor GitHub metadata
github-meta = {
script = pkgs.writers.writeDash "github-meta" ''
${pkgs.curl}/bin/curl -sSL https://api.github.com/meta | ${pkgs.jq}/bin/jq
'';
frequency = "*:0/5"; # Every 5 minutes
reporters = [
# Report changes to Telegram
(pkgs.writers.writeDash "telegram-reporter" ''
${pkgs.curl}/bin/curl -X POST https://api.telegram.org/bot''${TOKEN}/sendMessage \
-d chat_id=123456 \
-d text="$(cat)"
'')
# Also show desktop notifications
(pkgs.writers.writeDash "notify" ''
${pkgs.libnotify}/bin/notify-send "$PANOPTIKON_WATCHER has changed."
'')
];
};
# Monitor a website for specific content
nixos-updates = {
script = pkgs.panoptikon.urlSelector "#news h2" "https://nixos.org/blog/";
frequency = "daily";
reporters = [
# Report to IRC
(pkgs.panoptikon.kpaste-irc {
target = "#nixos";
server = "irc.libera.chat";
messagePrefix = "New NixOS blog post: ";
})
];
};
# Monitor a local command
disk-space = {
script = pkgs.writers.writeDash "disk-space" ''
df -h / | tail -1 | awk '{print $5 " used
}'';
frequency = "*:0/30"; # Every 30 minutes
reporters = [
# Log to systemd journal
(pkgs.writers.writeDash "journal-log" ''
journalctl -t panoptikon-disk-space --since "1 hour ago" | tail -5
'')
];
};
services.panoptikon.watchers = {
example = {
script = pkgs.panoptikonWatchers.plain "https://example.com";
frequency = "hourly";
reporters = [ (pkgs.panoptikonReporters.wall { }) ];
};
};
```
3. **Deploy and monitor**:
```bash
# Check status
sudo systemctl status panoptikon-example
# View logs
sudo journalctl -u panoptikon-example -f
# Trigger manually
sudo systemctl start panoptikon-example
```
## How It Works
```
┌─────────────┐
│ systemd │
│ timer │─── triggers ───┐
└─────────────┘ │
┌─────────────────────┐
│ Panoptikon Service │
│ (oneshot) │
└─────────────────────┘
┌─────────┴─────────┐
▼ │
┌──────────────────┐ │
│ Run watcher │ │
│ script → current │ │
└──────────────────┘ │
│ │
▼ │
┌──────────────────┐ │
│ Compare with │ │
│ .old state │ │
└──────────────────┘ │
│ │
diff? ─┼─── Yes ────────┤
│ No │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Exit quietly │ │ Pipe diff to │
└──────────────┘ │ reporters │
└──────┬───────┘
┌────────────┴────────────┐
▼ ▼
[reporter 1] [reporter 2]
│ │
└──────────┬───────────────┘
[Notifications sent]
┌──────────────────┐
│ Rotate state: │
│ current → old │
└──────────────────┘
```
**Key Points:**
- Each watcher runs as its own systemd service with a dedicated timer
- Output is saved to `/var/lib/panoptikon/<watcher-name>` and `<watcher-name>.old`
- Only diffs are sent to reporters (full output never transmitted)
- Reporters run in a clean, empty `/run` directory for isolation
- State persists across reboots
## Configuration Reference
### Watcher Options
| Option | Type | Description |
|--------|------|-------------|
| `script` | path | **Required.** Executable whose stdout will be monitored. |
| `frequency` | string | systemd.time(7) timer expression. Default: `"daily"` |
| `reporters` | list of paths | **Required.** Scripts that receive the diff via stdin. |
| `loadCredential` | list of strings | Credentials to pass from systemd (`LoadCredential=`). |
### Timer Syntax
Common patterns:
| Pattern | Meaning |
|---------|---------|
| `*:0/5` | Every 5 minutes |
| `*:0/15` | Every 15 minutes |
| `hourly` | At minute 0 of every hour |
| `daily` | Once per day at midnight |
| `*-*-1 0:0:0` | First day of month |
| `Mon *-*-* 0:0:0` | Every Monday |
| `Sat,Sun *-*-* 0:0:0` | Weekends |
See: `man systemd.time`
### Environment Variables
- `PANOPTIKON_WATCHER` - Name of the watcher (available to both watchers and reporters)
## Built-in Helpers
The overlay provides convenient watcher and reporter constructors:
### Watcher Helpers
```nix
# Fetch raw content
pkgs.panoptikonWatchers.plain "https://example.com"
# Convert HTML to plain text
pkgs.panoptikonWatchers.html "https://example.com"
# Extract specific HTML elements using CSS selector
pkgs.panoptikonWatchers.htmlSelector "#news h2" "https://example.com"
# Process JSON with jq
pkgs.panoptikonWatchers.json { jqScript = ".data[] | .value" } "https://api.example.com"
```
### Reporter Helpers
```nix
# Send to wall (broadcast to all logged-in users)
pkgs.panoptikonReporters.wall { }
# Send email
pkgs.panoptikonReporters.mail {
recipient = "admin@example.org";
subjectPrefix = "[Alert]";
}
# Telegram Bot API
pkgs.panoptikonReporters.telegram {
chatId = "123456";
tokenPath = "/run/keys/telegram-token"; # Use LoadCredential=
messagePrefix = "Change detected: ";
}
# Matrix (via REST API)
pkgs.panoptikonReporters.matrix {
homeserver = "https://matrix.org";
roomId = "!roomid:matrix.org";
tokenPath = "/run/keys/matrix-token";
}
# IRC (simple netcat-based)
pkgs.panoptikonReporters.irc {
target = "#channel";
server = "irc.libera.chat";
port = "6667";
nick = "panoptikon-bot";
}
# kpaste + ircsink (for retiolum)
pkgs.panoptikonReporters.kpaste-irc {
target = "#nixos";
server = "irc.r";
retiolumLink = true; # Generate retiolum link
}
```
## Service Management
**Important:** For security, always use `LoadCredential=` for tokens instead of embedding them in your Nix store.
### systemd Integration
## Advanced Examples
Each watcher gets its own systemd service and timer:
### Monitor Bitcoin Price with Telegram Alert
See [examples/bitcoin.nix](./examples/bitcoin.nix)
### Website Content Change with Email Notification
See [examples/nixos.nix](./examples/nixos.nix)
### System Metrics with Console Notifications
See [examples/system.nix](./examples/system.nix)
### Custom Script with Multiple Reporters
See [examples/simple.nix](./examples/simple.nix) for more.
## Testing
### Run a VM with Example Configurations
```bash
# List all Panoptikon services
systemctl list-units "panoptikon-*"
# Check a specific watcher
systemctl status panoptikon-github-meta
# View logs
journalctl -u panoptikon-github-meta -f
# Trigger a manual run
systemctl start panoptikon-github-meta
nix run .#panoptikon-vm
```
### Timer Configuration
This builds and boots a NixOS VM with all example watchers pre-configured.
Timers use systemd timer syntax. Common examples:
### Inside the VM
- `*:0/5` - Every 5 minutes
- `daily` - Once per day
- `*:0/15` - Every 15 minutes
- `weekly` - Once per week
```bash
# List all panoptikon services
systemctl list-units "panoptikon-*"
See [systemd.time(7)](https://www.freedesktop.org/software/systemd/man/systemd.time.html) for full syntax.
# Check Bitcoin watcher status
systemctl status panoptikon-bitcoin-price
## Security Considerations
# Follow its logs
journalctl -u panoptikon-bitcoin-price -f
- Watchers run as the `panoptikon` system user
- Scripts are executed in `/var/lib/panoptikon`
- Use `LoadCredential=` to securely pass secrets
- Scripts should be written defensively (use `set -euo pipefail`)
# Force a run (useful for testing)
systemctl start panoptikon-bitcoin-price
## Troubleshooting
# Check state directory
ls -la /var/lib/panoptikon/
```
## Examples
### Manual Script Testing
See the [examples directory](./examples/) for complete configurations.
Run the watcher script as the panoptikon user to see its output:
Run `nix run .#panoptikon-vm` to start a VM with Panoptikon and example watchers pre-configured.
```bash
sudo -u panoptikon /nix/store/<hash>-watch-bitcoin
```
Check that it produces clean output without errors.
## FAQ
**Q: Can Panoptikon monitor FTP or SSH?**
A: Yes! Write a custom watcher script that uses `curl` (for SFTP), `ssh`, or any CLI tool. Example:
```nix
script = pkgs.writers.writeDash "ssh-check" ''
${pkgs.openssh}/bin/ssh user@host "uptime"
'';
```
**Q: What happens if the watcher script fails?**
A: The service will be marked as failed and will restart on next timer activation (unless configured otherwise). The error is logged to the systemd journal. Reporters are skipped.
**Q: Can I run multiple reporters for the same event?**
A: Yes! Reporters are executed sequentially. If one fails, others still run (errors are suppressed with `|| :`).
**Q: How do I handle JSON pretty-printing?**
A: Use jq:
```nix
script = pkgs.panoptikonWatchers.json { jqScript = "." } "https://api.example.com/data";
```
Or in a custom script:
```bash
curl -s ... | jq -S . # -S sorts keys
```
**Q: Can I send rich formatting (HTML, Markdown) to reporters?**
A: Yes! Reporters receive the raw diff. For IRC, use colors sparingly. For Telegram/Matrix, you can send Markdown or HTML by constructing appropriate payloads in custom reporters.