refactor: Convert Nix setup to classic Nix (non-flake)

This replaces the experimental flake-based Nix configuration with
a classic Nix setup (`default.nix`, `shell.nix`, `module.nix`)
per user request.

Changes include:
- Removing `flake.nix`.
- Creating `default.nix` to package the python bridge.
- Creating `shell.nix` for a reproducible development environment.
- Creating `module.nix` for the NixOS systemd service.
- The `module.nix` now uses systemd's `LoadCredential=` to safely
  expose the XMPP password to the bridge daemon running as a dynamic
  user, resolving permission issues.
- `README.md` instructions have been fully rewritten to focus on classic
  Nix usage, answering user questions on secret management and repository
  cloning structure.

Co-authored-by: jamessucla <2191476+jamessucla@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot]
2026-03-06 07:29:27 +00:00
parent 1a71806a54
commit 50b76aabe7
5 changed files with 97 additions and 118 deletions

View File

@@ -12,47 +12,41 @@ If the internet goes down, locals can communicate over the Meshtastic LoRa mesh.
* **The Federated Layer:** XMPP server facilitating connections globally.
## Prerequisites
- A local NixOS installation with flakes enabled.
- A local NixOS installation.
- A Meshtastic device connected via USB to the NixOS machine.
- An XMPP account that can join MUCs.
## Usage
### Using the Nix Flake directly
You can run the python bridge straight from the flake:
```bash
nix run . -- -j "your_jid@xmpp.org" -p "your_password" -r "your_room@conference.xmpp.org" -n "meshbridge"
```
### Developing
You can drop into a Nix shell with all the required python dependencies:
```bash
nix develop
nix-shell
```
From here you can run the bridge directly:
```bash
sovereign-bridge -j "your_jid@xmpp.org" -p "your_password" -r "your_room@conference.xmpp.org" -n "meshbridge"
```
### NixOS Module (Systemd Service)
SovereignRelay provides a NixOS module to seamlessly integrate the bridge as a declarative `systemd` service that will persist, automatically start on boot, and autorestart on failure.
Include the flake in your NixOS configuration's `flake.nix` inputs:
Clone this repository to your NixOS machine:
```nix
{
inputs.sovereign-relay.url = "github:jshiffer/lora-xmpp-bridge";
# ...
}
```bash
git clone https://github.com/jshiffer/lora-xmpp-bridge.git /path/to/lora-xmpp-bridge
```
Then in your NixOS configuration (e.g., `configuration.nix`):
Then in your NixOS configuration (e.g., `/etc/nixos/configuration.nix`), import the `module.nix` file:
```nix
{
imports = [
inputs.sovereign-relay.nixosModules.default
/path/to/lora-xmpp-bridge/module.nix
];
services.sovereign-bridge = {
@@ -82,11 +76,11 @@ sudo chmod 600 /run/secrets/xmpp_password
#### Reproducing from a Fresh NixOS Install
To deploy this on a fresh NixOS system for the hackathon:
To deploy this on a fresh NixOS system for the hackathon without experimental features:
1. Connect your Meshtastic node via USB.
2. Ensure flakes are enabled on your fresh install (add `nix.settings.experimental-features = [ "nix-command" "flakes" ];` to your configuration).
3. Create your configuration flake (e.g., in `/etc/nixos/flake.nix`) that includes the `sovereign-bridge` module and configuration block as shown above.
2. Clone this repository to the machine: `git clone https://github.com/jshiffer/lora-xmpp-bridge.git /etc/nixos/lora-xmpp-bridge`.
3. Edit your `/etc/nixos/configuration.nix` to include the module and configuration block as shown above.
4. Create the password file: `echo "yourpassword" | sudo tee /run/secrets/xmpp_password && sudo chmod 600 /run/secrets/xmpp_password`.
5. Apply the configuration: `sudo nixos-rebuild switch --flake /etc/nixos#yourhostname`.
6. Verify it's running: `systemctl status sovereign-bridge.service`.
5. Apply the configuration: `sudo nixos-rebuild switch`.
6. Verify it's running: `systemctl status sovereign-bridge.service`.

12
default.nix Normal file
View File

@@ -0,0 +1,12 @@
{ pkgs ? import <nixpkgs> {} }:
let
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
meshtastic
slixmpp
]);
in
pkgs.writeScriptBin "sovereign-bridge" ''
#!${pythonEnv}/bin/python
${builtins.readFile ./bridge.py}
''

View File

@@ -1,95 +0,0 @@
{
description = "SovereignRelay - Meshtastic to XMPP Bridge";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
# Define the Python environment with required packages
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
meshtastic
slixmpp
]);
# Package the bridge script
sovereign-bridge = pkgs.writeScriptBin "sovereign-bridge" ''
#!${pythonEnv}/bin/python
${builtins.readFile ./bridge.py}
'';
in
{
packages.default = sovereign-bridge;
devShells.default = pkgs.mkShell {
buildInputs = [
pythonEnv
sovereign-bridge
];
};
}
) // {
nixosModules.default = { config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.sovereign-bridge;
in {
options.services.sovereign-bridge = {
enable = mkEnableOption "SovereignRelay Bridge";
jid = mkOption {
type = types.str;
description = "XMPP JID for the bridge bot";
};
passwordFile = mkOption {
type = types.path;
description = "Path to file containing XMPP password";
};
room = mkOption {
type = types.str;
description = "XMPP MUC room to bridge";
};
nick = mkOption {
type = types.str;
default = "meshbridge";
description = "Nickname for the bridge bot in the MUC";
};
};
config = mkIf cfg.enable {
systemd.services.sovereign-bridge = {
description = "SovereignRelay Meshtastic to XMPP Bridge";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = let
script = pkgs.writeShellScript "sovereign-bridge-start" ''
# Run the bridge
${self.packages.${pkgs.system}.default}/bin/sovereign-bridge \
-j "${cfg.jid}" \
-P "${cfg.passwordFile}" \
-r "${cfg.room}" \
-n "${cfg.nick}"
'';
in "${script}";
Restart = "always";
RestartSec = "10";
# Required to access serial ports for Meshtastic
SupplementaryGroups = [ "dialout" ];
DynamicUser = true;
};
};
};
};
};
}

61
module.nix Normal file
View File

@@ -0,0 +1,61 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.sovereign-bridge;
# Import the package defined in default.nix
sovereign-bridge = import ./default.nix { inherit pkgs; };
in {
options.services.sovereign-bridge = {
enable = mkEnableOption "SovereignRelay Bridge";
jid = mkOption {
type = types.str;
description = "XMPP JID for the bridge bot";
};
passwordFile = mkOption {
type = types.path;
description = "Path to file containing XMPP password";
};
room = mkOption {
type = types.str;
description = "XMPP MUC room to bridge";
};
nick = mkOption {
type = types.str;
default = "meshbridge";
description = "Nickname for the bridge bot in the MUC";
};
};
config = mkIf cfg.enable {
systemd.services.sovereign-bridge = {
description = "SovereignRelay Meshtastic to XMPP Bridge";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
LoadCredential = "xmpp_password:${cfg.passwordFile}";
ExecStart = let
script = pkgs.writeShellScript "sovereign-bridge-start" ''
# Run the bridge
${sovereign-bridge}/bin/sovereign-bridge \
-j ${lib.escapeShellArg cfg.jid} \
-P "$CREDENTIALS_DIRECTORY/xmpp_password" \
-r ${lib.escapeShellArg cfg.room} \
-n ${lib.escapeShellArg cfg.nick}
'';
in "${script}";
Restart = "always";
RestartSec = "10";
# Required to access serial ports for Meshtastic
SupplementaryGroups = [ "dialout" ];
DynamicUser = true;
};
};
};
}

7
shell.nix Normal file
View File

@@ -0,0 +1,7 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = [
(import ./default.nix { inherit pkgs; })
];
}