Files
panoptikon/README.md
2026-02-20 17:23:01 +01:00

339 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Panoptikon Watch the world from NixOS
![](./panoptikon.jpg)
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.
## 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
## Installation
Add Panoptikon to your NixOS configuration:
```nix
{ config, pkgs, ... }:
{
imports = [
# Add the Panoptikon module
(pkgs.callPackage (builtins.fetchTarball https://github.com/kfm/panoptikon-library/archive/main.tar.gz) { }).nixosModules.default
];
# Enable Panoptikon service
services.panoptikon.enable = true;
# Configure your watchers
services.panoptikon.watchers = {
# Your watcher configurations go here
};
}
```
## Configuration
### Basic Watcher Configuration
```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
''')
];
};
};
}
```
### Advanced Configuration
#### Using Secrets
```nix
{
services.panoptikon.watchers = {
private-api = {
script = pkgs.writers.writeDash "private-api" '''
${pkgs.curl}/bin/curl -sSL \
-H "Authorization: Bearer $API_TOKEN" \
https://api.example.com/data
''';
frequency = "hourly";
loadCredential = [ "API_TOKEN" ];
reporters = [
(pkgs.writers.writeDash "secure-reporter" '''
${pkgs.curl}/bin/curl -X POST https://secure.example.com/report \
-d "$(cat)"
''')
];
};
};
}
```
#### JSON API Monitoring
```nix
{
services.panoptikon.watchers = {
crypto-prices = {
script = pkgs.panoptikon.urlJSON {
jqScript = ".[0] | { name: .name, price: .quote.USD.price }";
} "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=bitcoin";
frequency = "*:0/15"; # Every 15 minutes
reporters = [
(pkgs.writers.writeDash "price-alert" '''
price=$(echo "$(cat)" | ${pkgs.jq}/bin/jq -r '.price')
if (( $(echo "$price > 60000" | bc -l) )); then
${pkgs.libnotify}/bin/notify-send "Bitcoin price: $$price"
fi
''')
];
};
};
}
```
## Available Helper Scripts
Panoptikon provides several helper scripts in the overlay:
### URL Monitoring
- `pkgs.panoptikon.url`: Fetch and convert HTML to text
- `pkgs.panoptikon.urlSelector`: Extract specific HTML elements using CSS selectors
- `pkgs.panoptikon.urlJSON`: Fetch JSON and apply jq transformations
### Reporters
- `pkgs.panoptikon.kpaste-irc`: Report changes to IRC via kpaste
- `target`: IRC target (channel or user)
- `server`: IRC server (default: irc.r)
- `messagePrefix`: Prefix for messages (default: "change detected: ")
- `nick`: Nick to use (default: watcher name + "-watcher")
- `retiolumLink`: Use retiolum link (default: false)
## Service Management
### Systemd Integration
Each watcher gets its own systemd service and timer:
```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
```
### Timer Configuration
Timers use systemd.timer syntax. Common examples:
- `*:0/5` - Every 5 minutes
- `daily` - Once per day
- `Mon..Fri 9:00-17:00` - Weekdays during business hours
- `*:0/15` - Every 15 minutes
- `weekly` - Once per week
See [systemd.time(7)](https://www.freedesktop.org/software/systemd/man/systemd.time.html) for full syntax.
## Security Considerations
- 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`)
## Troubleshooting
### Common Issues
1. **No output**: Check if the script runs correctly manually
2. **Permission denied**: Ensure the panoptikon user can access required resources
3. **Network issues**: Add `network-online.target` as a dependency
4. **Rate limiting**: Add randomized delays using `RandomizedDelaySec`
### Debug Mode
Enable debug logging:
```nix
services.panoptikon.watchers.github-meta = {
# ...
script = ''
set -x # Enable debug output
${pkgs.curl}/bin/curl -sSL https://api.github.com/meta | ${pkgs.jq}/bin/jq
'';
};
```
## Examples
### Monitor Cryptocurrency Prices
```nix
{
services.panoptikon.enable = true;
services.panoptikon.watchers = {
bitcoin-price = {
script = pkgs.panoptikon.urlJSON {
jqScript = ".[0] | { name: .name, price: .quote.USD.price, change: .quote.USD.percent_change_24h }";
} "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=bitcoin";
frequency = "*:0/30";
reporters = [
(pkgs.writers.writeDash "btc-alert" '''
price=$(echo "$(cat)" | ${pkgs.jq}/bin/jq -r '.price')
change=$(echo "$(cat)" | ${pkgs.jq}/bin/jq -r '.change')
${pkgs.libnotify}/bin/notify-send \
"Bitcoin: $$price ($$change%)"
''')
];
};
};
}
```
### Monitor System Metrics
```nix
{
services.panoptikon.enable = true;
services.panoptikon.watchers = {
load-average = {
script = pkgs.writers.writeDash "load-average" '''
uptime | awk -F'load average:' '{print $2}' | awk '{print $1 " (1m), " $2 " (5m), " $3 " (15m)"}'
''';
frequency = "*:0/2"; # Every 2 minutes
reporters = [
(pkgs.writers.writeDash "load-alert" '''
load=$(echo "$(cat)" | awk '{print $1}' | tr -d ',')
if (( $(echo "$load > 2.0" | bc -l) )); then
${pkgs.libnotify}/bin/notify-send "High load: $$load"
fi
''')
];
};
};
}
```
### Monitor NixOS Updates
```nix
{
services.panoptikon.enable = true;
services.panoptikon.watchers = {
nixos-updates = {
script = pkgs.panoptikon.url "https://nixos.org/blog/";
frequency = "daily";
reporters = [
(pkgs.writers.writeDash "update-notify" '''
${pkgs.libnotify}/bin/notify-send "NixOS blog updated: $(cat | head -5)"
''')
];
};
};
}
```
## Development
### Adding Custom Reporters
Create a new reporter in the overlay:
```nix
final: prev: {
panoptikon = prev.panoptikon // {
my-custom-reporter = {
endpoint,
authToken ? "",
...
}:
prev.writers.writeDash "my-reporter" ''
${prev.curl}/bin/curl -X POST ''${endpoint} \
-H "Authorization: Bearer ''${authToken} \
-d "$(cat)"
'';
};
};
```
### Contributing
1. Fork the repository
2. Create a feature branch
3. Add tests if applicable
4. Submit a pull request
## License
MIT License