Skip to content

Commit 2d07315

Browse files
committed
nixos/meshtasticd: init
1 parent c7da5e7 commit 2d07315

File tree

6 files changed

+231
-1
lines changed

6 files changed

+231
-1
lines changed

nixos/doc/manual/release-notes/rl-2605.section.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
1212

13-
- Create the first release note entry in this section!
13+
- [Meshtastic](https://meshtastic.org), an open-source, off-grid, decentralised mesh network
14+
designed to run on affordable, low-power devices. Available as [services.meshtasticd]
15+
(#opt-services.meshtasticd.enable).
1416

1517
## Backward Incompatibilities {#sec-release-26.05-incompatibilities}
1618

nixos/modules/module-list.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,7 @@
12421242
./services/networking/lxd-image-server.nix
12431243
./services/networking/magic-wormhole-mailbox-server.nix
12441244
./services/networking/matterbridge.nix
1245+
./services/networking/meshtasticd.nix
12451246
./services/networking/microsocks.nix
12461247
./services/networking/mihomo.nix
12471248
./services/networking/minidlna.nix
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Meshtasticd {#module-services-meshtasticd}
2+
3+
[Meshtasticd](https://meshtastic.org/) daemon.
4+
5+
Meshtastic is an open-source, off-grid, decentralised mesh network designed to
6+
run on affordable, low-power devices.
7+
8+
Meshtastic is a project that enables you to use inexpensive LoRa radios as a
9+
long range off-grid communication platform in areas without existing or reliable
10+
communications infrastructure. This project is 100% community driven and open
11+
source!
12+
13+
## Quickstart {#module-services-meshtasticd-quickstart}
14+
15+
A minimal configuration:
16+
17+
```nix
18+
{
19+
services.meshtasticd = {
20+
enable = true;
21+
port = 4403;
22+
settings = {
23+
Lora = {
24+
Module = "auto";
25+
};
26+
Webserver = {
27+
Port = 9443;
28+
RootPath = pkgs.meshtastic-web;
29+
};
30+
General = {
31+
MaxNodes = 200;
32+
MaxMessageQueue = 100;
33+
MACAddressSource = "eth0";
34+
};
35+
};
36+
};
37+
}
38+
```
39+
40+
By default Meshtasticd listens on all network interfaces. The example above
41+
binds the daemon to port `4403` and the web UI to `9443`. This module
42+
intentionally does not configure an reverse proxy for you, keeping the module
43+
focused on the Meshtastic service itself. If you need to restrict access, use
44+
firewall rules or put the web UI behind a reverse proxy (e.g.: Caddy, Nginx)
45+
that binds to `127.0.0.1` and exposes only the proxy. This approach leaves proxy
46+
choice and TLS configuration to the operator while documenting how to securely
47+
expose the web UI when required.
48+
49+
## Configuration {#module-services-meshtasticd-config}
50+
51+
All available configuration directives are documented in the
52+
[standard Meshtastic configuration file](https://github.com/meshtastic/firmware/blob/develop/bin/config-dist.yaml).
53+
54+
The service uses a dedicated user and group account (`meshtasticd`) by default.
55+
If you override the service user, ensure it is a member of the `spi` and `gpio`
56+
groups so it can access the required hardware devices, as mandated by
57+
Meshtastic’s default `udev` rules.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
7+
8+
let
9+
cfg = config.services.meshtasticd;
10+
format = pkgs.formats.yaml { };
11+
configFile = format.generate "config.yaml" cfg.settings;
12+
in
13+
{
14+
options.services.meshtasticd = {
15+
enable = lib.mkEnableOption "Meshtastic daemon";
16+
package = lib.mkPackageOption pkgs "meshtasticd" { };
17+
18+
user = lib.mkOption {
19+
default = "meshtasticd";
20+
description = "User meshtasticd runs as.";
21+
type = lib.types.str;
22+
};
23+
24+
group = lib.mkOption {
25+
default = "meshtasticd";
26+
description = "Group meshtasticd runs as.";
27+
type = lib.types.str;
28+
};
29+
30+
port = lib.mkOption {
31+
type = lib.types.port;
32+
default = 4403;
33+
description = "Port to listen on";
34+
};
35+
36+
settings = lib.mkOption {
37+
type = format.type;
38+
example = lib.literalExpression ''
39+
Lora = {
40+
Module = "auto";
41+
};
42+
Webserver = {
43+
Port = 9443;
44+
RootPath = pkgs.meshtastic-web;
45+
};
46+
General = {
47+
MaxNodes = 200;
48+
MaxMessageQueue = 100;
49+
MACAddressSource = "eth0";
50+
};
51+
'';
52+
description = ''
53+
The Meshtastic configuration file.
54+
55+
An example of configuration can be found at <https://github.com/meshtastic/firmware/blob/develop/bin/config-dist.yaml>
56+
'';
57+
};
58+
59+
dataDir = lib.mkOption {
60+
default = "/var/lib/meshtasticd";
61+
type = lib.types.path;
62+
description = ''
63+
The data directory.
64+
'';
65+
};
66+
};
67+
68+
config = lib.mkIf cfg.enable {
69+
# Creation of the `meshtasticd` privilege user.
70+
users = {
71+
users = lib.mkIf (cfg.user == "meshtasticd") {
72+
meshtasticd = {
73+
home = cfg.dataDir;
74+
description = "meshtasticd-daemon privilege user";
75+
group = cfg.group;
76+
isSystemUser = true;
77+
extraGroups = [
78+
"spi"
79+
"gpio"
80+
];
81+
};
82+
};
83+
groups = lib.mkIf (cfg.group == "meshtasticd") {
84+
meshtasticd = { };
85+
# These groups are required for udev rules to work properly.
86+
spi = { };
87+
gpio = { };
88+
};
89+
};
90+
91+
# The `meshtasticd` package provides udev rules.
92+
services.udev.packages = [
93+
cfg.package
94+
];
95+
96+
# Creation of the `meshtasticd` service.
97+
# Based on the official meshtasticd service file: https://github.com/meshtastic/firmware/blob/develop/bin/meshtasticd.service
98+
systemd.services.meshtasticd = {
99+
description = "Meshtastic Native Daemon";
100+
after = [
101+
"network-online.target"
102+
"network.target"
103+
];
104+
wants = [
105+
"network-online.target"
106+
"network.target"
107+
];
108+
wantedBy = [ "multi-user.target" ];
109+
110+
serviceConfig = {
111+
User = cfg.user;
112+
Group = cfg.group;
113+
Type = "simple";
114+
StateDirectory = "meshtasticd";
115+
AmbientCapabilities = [
116+
"CAP_NET_BIND_SERVICE"
117+
];
118+
ExecStart = "${lib.getExe cfg.package} --port=${builtins.toString cfg.port} --fsdir=${cfg.dataDir} --config=${configFile} --verbose";
119+
Restart = "always";
120+
RestartSec = "3";
121+
};
122+
};
123+
};
124+
}

nixos/tests/all-tests.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,7 @@ in
937937
meilisearch = runTest ./meilisearch.nix;
938938
memcached = runTest ./memcached.nix;
939939
merecat = runTest ./merecat.nix;
940+
meshtasticd = runTest ./networking/meshtasticd.nix;
940941
metabase = runTest ./metabase.nix;
941942
mihomo = runTest ./mihomo.nix;
942943
mimir = runTest ./mimir.nix;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
lib,
3+
pkgs,
4+
...
5+
}:
6+
let
7+
mainPort = 9445;
8+
webPort = 9446;
9+
in
10+
{
11+
name = "meshtasticd";
12+
meta.maintainers = [ lib.maintainers.drupol ];
13+
14+
nodes.machine = {
15+
services.meshtasticd = {
16+
enable = true;
17+
port = mainPort;
18+
19+
settings = {
20+
Lora = {
21+
Module = "sim";
22+
DIO2_AS_RF_SWITCH = false;
23+
spiSpeed = "2000000";
24+
};
25+
Webserver = {
26+
Port = webPort;
27+
RootPath = pkgs.meshtastic-web;
28+
};
29+
General = {
30+
MaxNodes = 200;
31+
MaxMessageQueue = 100;
32+
MACAddressSource = "eth0";
33+
};
34+
};
35+
};
36+
};
37+
38+
testScript = ''
39+
with subtest("Test meshtasticd service"):
40+
machine.wait_for_unit("meshtasticd.service")
41+
machine.wait_for_open_port(${builtins.toString mainPort})
42+
machine.wait_for_open_port(${builtins.toString webPort})
43+
machine.succeed("curl -fvvv -Ls http://localhost:${builtins.toString webPort} | grep -q 'Meshtastic Web Client'")
44+
'';
45+
}

0 commit comments

Comments
 (0)