4.7 KiB
4.7 KiB
AGENTS.md
This file provides guidance for AI coding assistants working on the Panoptikon project.
Project Overview
Panoptikon is a NixOS module that monitors website content and command output changes. It periodically runs scripts, compares outputs, and reports changes to various destinations.
Key Components:
flake.nix- Flake definition with modules, overlays, and VM appnix/module.nix- Main NixOS module implementationnix/overlay.nix- Overlay providingpanoptikonWatchersandpanoptikonReportershelpersexamples/- Example configurationsREADME.md- User-facing documentation
Coding Conventions
Nix Code Style
- Use Nix expression language (not NixOS modules have
libavailable) - Prefer
lib.attrsets.mapAttrs'withlib.nameValuePairfor transforming attrsets - Use
lib.strings.concatMapStringsSepfor joining strings with separators - Maintain consistent formatting (2-space indentation)
Systemd Configuration
- Services run as dedicated
panoptikonsystem user - Use
RuntimeDirectoryfor temporary per-run isolation - Use
ReadWritePathsto restrict filesystem access - Enable hardening flags:
ProtectSystem,ProtectHome,PrivateTmp,NoNewPrivileges
State Management
- State files stored in
/var/lib/panoptikon/ - Each watcher uses
${watcherName}and${watcherName}.old - Scripts ensure
.oldexists before diff - Rotate state after comparing
Security Practices
- Never log sensitive data (tokens, credentials)
- Use
set -efuin shell scripts (avoid-x) - Use
LoadCredential=for passing secrets - Validate file existence before reading tokens
- Escape shell arguments with
lib.escapeShellArg
Testing
Manual Testing
-
Start the VM with all examples:
nix run .#panoptikon-vm -
Inside the VM, check service status:
systemctl status panoptikon-* journalctl -u panoptikon-bitcoin-price -f -
Trigger a manual run:
systemctl start panoptikon-bitcoin-price
Linting and Formatting
# Check Nix syntax
nix flake check
# Format Nix code
nix fmt
Debugging
Service Issues
-
Check if service is enabled and running:
systemctl status panoptikon-<watcher-name> -
View logs:
journalctl -u panoptikon-<watcher-name> -f -
Test scripts manually (as panoptikon user):
sudo -u panoptikon /nix/store/...-script
State File Issues
- Check state directory:
/var/lib/panoptikon/ - State files persist across reboots
- Delete state file to force change detection on next run
Timer Issues
- Timer uses
RandomizedDelaySecof 1 hour to prevent thundering herd - This means services may be delayed up to 1 hour from the scheduled time
- Check active timers:
systemctl list-timers "panoptikon-*"
Architecture Notes
Data Flow
- systemd timer triggers service
- Script runs, stdout saved to
${watcherName} - Diff against
${watcherName}.old - If changes detected: pipe diff to each reporter
- Rotate: current → old
Reporter Contract
- Reporters receive the diff via stdin
- Can access watcher name via
$PANOPTIKON_WATCHER - Should handle errors gracefully (exit code ignored in main script)
- Run in isolated empty
/run/panoptikon/<watcher-name>directory
Watcher Contract
- Must be an executable path (script or binary)
- Output is stored verbatim for diffing
- Should be idempotent (running twice produces same output)
- Avoid side effects; output only relevant data
Extension Points
Adding New Watcher Helpers
Add to panoptikonWatchers in overlay.nix:
panoptikonWatchers = (prev.panoptikonWatchers or { }) // {
myWatcher =
args:
prev.writers.writeDash "watch-my" ''
${prev.somePackage}/bin/tool --option ${lib.escapeShellArg args}
'';
};
Adding New Reporter Helpers
Similar pattern in panoptikonReporters:
panoptikonReporters = (prev.panoptikonReporters or { }) // {
myReporter =
{ requiredArg, optionalArg ? "default" }:
prev.writers.writeDash "report-my" ''
cat | ${prev.tool}/bin/send --to ''${requiredArg}
'';
};
Common Dependencies
writers.writeDash- Write shell scriptscurl- HTTP requestsjq- JSON processinghtmlq- HTML selectorsdiffutils- Compare outputs
Known Limitations
- Large outputs may cause memory pressure in diff
- No built-in rate limiting for external APIs
- Reporters cannot access the original script output, only the diff
- State stored in plain files (no compression/rotation)
Future Considerations
- Adding watchdog timeouts
- Metrics collection (execution time, success rate)
- Backoff strategies for failing watchers
- Support for structured output formats (JSON events)