diff --git a/.gitignore b/.gitignore index 5dad936..53da3d2 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,4 @@ Thumbs.db # Testing directory (not committed to repo) tests/ +nixos-test/ diff --git a/NIXOS.md b/NIXOS.md new file mode 100644 index 0000000..7c1f755 --- /dev/null +++ b/NIXOS.md @@ -0,0 +1,362 @@ +# NixOS Support + +This project includes a NixOS service module that allows you to run MeshCore Packet Capture as a systemd service on NixOS. + +## Quick Start + +### Recommended Configuration (Let'sMesh Analyzer) + +This configuration uploads packets to both Let's Mesh Analyzer servers (US and EU) for redundancy, plus an optional third MQTT broker for your own infrastructure: + +Add this to your `/etc/nixos/configuration.nix`: + +```nix +{ + imports = [ + (builtins.fetchTarball "https://github.com/agessaman/meshcore-packet-capture/archive/main.tar.gz") + ]; + + services.meshcore-packet-capture = { + enable = true; + connectionType = "ble"; # or "serial" or "tcp" + + # Connection settings (choose one based on connectionType) + # For BLE: + # bleAddress = "AA:BB:CC:DD:EE:FF"; # optional: specific device address + # bleDeviceName = "MeshCore Device"; # optional: device name to scan for + + # For Serial: + # serialPorts = [ "/dev/ttyUSB0" "/dev/ttyUSB1" ]; # list of ports to try + + # For TCP: + # tcpHost = "localhost"; # TCP server hostname + # tcpPort = 5000; # TCP server port + + # Let'sMesh Analyzer - US Server + mqtt1 = { + enabled = true; + server = "mqtt-us-v1.letsmesh.net"; + port = 443; + transport = "websockets"; + useTLS = true; + useAuthToken = true; + tokenAudience = "mqtt-us-v1.letsmesh.net"; + keepalive = 120; + }; + + # Let'sMesh Analyzer - EU Server + mqtt2 = { + enabled = true; + server = "mqtt-eu-v1.letsmesh.net"; + port = 443; + transport = "websockets"; + useTLS = true; + useAuthToken = true; + tokenAudience = "mqtt-eu-v1.letsmesh.net"; + keepalive = 120; + }; + + # Optional: Your own MQTT broker (uncomment and configure as needed) + # mqtt3 = { + # enabled = true; + # server = "mqtt.example.com"; + # port = 1883; + # username = "your_username"; + # password = "your_password"; + # # or use TLS: + # # port = 8883; + # # useTLS = true; + # }; + + # Device private key for Let'sMesh authentication + # The script automatically fetches the private key from the device if it supports + # ENABLE_PRIVATE_KEY_EXPORT. Only provide these if automatic fetching fails: + # privateKeyFile = "/path/to/your/private/key/file"; + # OR + # privateKey = "your_private_key_hex_string"; + + # Optional: Owner information for Let'sMesh Analyzer + # ownerPublicKey = "YOUR_64_CHAR_HEX_PUBLIC_KEY"; # 64 hex characters + # ownerEmail = "your.email@example.com"; # Email for Let'sMesh Analyzer + + # Optional: IATA code for topic templates + iata = "SEA"; # Replace with your IATA code + }; +} +``` + +Then rebuild your system: + +```bash +sudo nixos-rebuild switch +``` + +**Note:** For Let'sMesh Analyzer authentication, you need your device's private key. See the [Authentication](#authentication) section below for details. + +### Custom MQTT Broker Configuration + +If you prefer to use your own MQTT broker instead of (or in addition to) Let'sMesh Analyzer: + +```nix +services.meshcore-packet-capture = { + enable = true; + connectionType = "ble"; + + mqtt1 = { + enabled = true; + server = "mqtt.example.com"; + port = 1883; # or 8883 for TLS + username = "your_username"; + password = "your_password"; + # Optional: Enable TLS + # useTLS = true; + # tlsVerify = true; + }; +}; +``` + +## Using with Flakes + +If you're using Nix Flakes, add this to your `flake.nix`: + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + meshcore-packet-capture = { + url = "github:agessaman/meshcore-packet-capture"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, meshcore-packet-capture }: { + nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + meshcore-packet-capture.nixosModules.default + { + services.meshcore-packet-capture = { + enable = true; + package = meshcore-packet-capture.packages.${system}.default; + connectionType = "ble"; + + # Let'sMesh Analyzer - US Server + mqtt1 = { + enabled = true; + server = "mqtt-us-v1.letsmesh.net"; + port = 443; + transport = "websockets"; + useTLS = true; + useAuthToken = true; + tokenAudience = "mqtt-us-v1.letsmesh.net"; + keepalive = 120; + }; + + # Let'sMesh Analyzer - EU Server + mqtt2 = { + enabled = true; + server = "mqtt-eu-v1.letsmesh.net"; + port = 443; + transport = "websockets"; + useTLS = true; + useAuthToken = true; + tokenAudience = "mqtt-eu-v1.letsmesh.net"; + keepalive = 120; + }; + + # Device private key is automatically fetched from the device + # Only set these if automatic fetching fails: + # privateKeyFile = "/path/to/your/private/key/file"; + # privateKey = "your_private_key_hex_string"; + + # Optional: Owner information for Let'sMesh Analyzer + # ownerPublicKey = "YOUR_64_CHAR_HEX_PUBLIC_KEY"; + # ownerEmail = "your.email@example.com"; + + iata = "SEA"; + }; + } + ]; + }; + }; +} +``` + +## Configuration Options + +### Connection Settings + +```nix +services.meshcore-packet-capture = { + connectionType = "ble"; # or "serial" or "tcp" + bleAddress = "AA:BB:CC:DD:EE:FF"; # optional + bleDeviceName = "MeshCore Device"; # optional + serialPorts = [ "/dev/ttyUSB0" "/dev/ttyUSB1" ]; # for serial connection + tcpHost = "localhost"; # for TCP connection + tcpPort = 5000; # for TCP connection + timeout = 30; + maxConnectionRetries = 5; # 0 = infinite + connectionRetryDelay = 5; + healthCheckInterval = 30; +}; +``` + +### MQTT Brokers + +You can configure up to 4 MQTT brokers. Here's an example with Let'sMesh Analyzer (recommended) plus a custom broker: + +```nix +services.meshcore-packet-capture = { + # Let'sMesh Analyzer - US Server + mqtt1 = { + enabled = true; + server = "mqtt-us-v1.letsmesh.net"; + port = 443; + transport = "websockets"; + useTLS = true; + useAuthToken = true; + tokenAudience = "mqtt-us-v1.letsmesh.net"; + keepalive = 120; + }; + + # Let'sMesh Analyzer - EU Server (for redundancy) + mqtt2 = { + enabled = true; + server = "mqtt-eu-v1.letsmesh.net"; + port = 443; + transport = "websockets"; + useTLS = true; + useAuthToken = true; + tokenAudience = "mqtt-eu-v1.letsmesh.net"; + keepalive = 120; + }; + + # Your own MQTT broker (optional) + mqtt3 = { + enabled = true; + server = "mqtt.example.com"; + port = 1883; # or 8883 for TLS + username = "user"; + password = "pass"; + transport = "tcp"; # or "websockets" + useTLS = false; # set to true for TLS + tlsVerify = true; + qos = 0; + retain = false; + keepalive = 60; + # Optional topic overrides + topicStatus = "meshcore/status"; + topicPackets = "meshcore/packets"; + topicRaw = "meshcore/raw"; + }; + + # mqtt4 can be configured similarly +}; +``` + +### Authentication + +For username/password authentication: + +```nix +services.meshcore-packet-capture = { + mqtt1 = { + username = "your_username"; + password = "your_password"; + }; +}; +``` + +For JWT token authentication: + +```nix +services.meshcore-packet-capture = { + mqtt1 = { + useAuthToken = true; + tokenAudience = "mqtt.example.com"; + }; + # Private key is automatically fetched from the device if it supports ENABLE_PRIVATE_KEY_EXPORT + # Only provide these if automatic fetching fails: + # privateKey = "your_private_key_hex_string"; + # OR + # privateKeyFile = "/path/to/private/key/file"; +}; +``` + +### Other Settings + +```nix +services.meshcore-packet-capture = { + logLevel = "INFO"; # DEBUG, INFO, WARNING, ERROR, CRITICAL + verbose = false; + debug = false; + enableMqtt = true; + maxMqttRetries = 5; # 0 = infinite + mqttRetryDelay = 5; + exitOnReconnectFail = true; + iata = "SEA"; # For topic templates + origin = "My Device"; + advertIntervalHours = 11; # 0 = disabled + uploadPacketTypes = [ 0 1 2 ]; # Filter packet types, null = all + rfDataTimeout = 15.0; + outputFile = null; # Optional output file path + # privateKeyFile = "/path/to/private/key/file"; # Only if auto-fetch fails + # privateKey = "hex_string"; # Only if auto-fetch fails + ownerPublicKey = null; # Optional: 64 hex character owner public key + ownerEmail = null; # Optional: Owner email for Let'sMesh Analyzer + dataDir = "/var/lib/meshcore-packet-capture"; + user = "meshcore"; + group = "meshcore"; +}; +``` + +## Permissions + +The service automatically adds the service user to the `bluetooth` and `dialout` groups for BLE and serial port access. + +## Development + +To enter a development shell with all dependencies: + +```bash +nix develop +``` + +## Troubleshooting + +### Package not found + +If you get an error about `meshcore` package not being found, you may need to update the hash in `nix/packages.nix`. The first time you build, Nix will tell you the correct hash to use. + +### BLE not working + +Ensure that: +1. Bluetooth is enabled: `services.bluetooth.enable = true;` +2. The service user has proper permissions (automatically handled) +3. Your Bluetooth adapter is properly configured + +### Serial port not accessible + +Ensure that: +1. The device exists: `ls -l /dev/ttyUSB0` +2. The service user is in the `dialout` group (automatically handled) +3. You've specified the correct port in `serialPorts` + +### Service logs + +View service logs with: + +```bash +journalctl -u meshcore-packet-capture -f +``` + +## Building the Package + +To build just the package (without installing as a service): + +```bash +nix build +``` + +The package will be available at `./result/bin/meshcore-packet-capture`. +