Skip to content

Commit bf672b9

Browse files
committed
fixup! Add Nix deployment support
Prevent unsafe API exposure and reject runtime paths that systemd cannot use. Preserve revision metadata for module deployments, and add Nix CI coverage for package and multi-instance service behavior. Generated with OpenAI Codex.
1 parent a264a01 commit bf672b9

5 files changed

Lines changed: 161 additions & 10 deletions

File tree

.github/workflows/nix.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Nix Checks
2+
3+
on: [ push, pull_request ]
4+
5+
permissions:
6+
contents: read
7+
8+
concurrency:
9+
group: ${{ github.workflow }}-${{ github.ref }}
10+
cancel-in-progress: true
11+
12+
jobs:
13+
check:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout source code
17+
uses: actions/checkout@v6
18+
- name: Install Nix
19+
uses: cachix/install-nix-action@v31
20+
- name: Check the flake
21+
run: nix flake check --print-build-logs

docs/nix.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,11 @@ Add the flake to your system inputs:
3636
enable = true;
3737
openFirewall = true;
3838
lightningPort = 9735;
39-
grpcPort = 3536;
4039
settings = {
4140
node = {
4241
network = "bitcoin";
4342
listening_addresses = [ "0.0.0.0:9735" ];
44-
grpc_service_address = "0.0.0.0:3536";
43+
grpc_service_address = "127.0.0.1:3536";
4544
};
4645
esplora.server_url = "https://mempool.space/api";
4746
log = {
@@ -79,6 +78,10 @@ stores data in `/var/lib/ldk-server/mainnet`.
7978
Use different Lightning and gRPC addresses for each instance. The module sets
8079
each data path even if the TOML file contains a different storage path.
8180

81+
The example exposes the Lightning port but keeps the gRPC API on loopback.
82+
Before you expose gRPC, configure its certificate and client access as
83+
described in [Operations - TLS](operations.md#tls).
84+
8285
The `settings` option writes values to the Nix store. Do not put passwords or
8386
other secrets in this option. Use `environmentFiles` for secrets:
8487

flake.nix

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,34 @@
2727
}
2828
);
2929

30-
checks = forAllSystems (system: {
31-
inherit (self.packages.${system}) ldk-server;
32-
});
30+
checks = forAllSystems (
31+
system:
32+
let
33+
pkgs = nixpkgs.legacyPackages.${system};
34+
in
35+
{
36+
inherit (self.packages.${system}) ldk-server;
37+
}
38+
// nixpkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
39+
nixos-module = pkgs.testers.runNixOSTest (
40+
import ./nix/tests/module.nix {
41+
inherit pkgs;
42+
ldkServerModule = self.nixosModules.ldk-server;
43+
}
44+
);
45+
}
46+
);
3347

3448
nixosModules = {
3549
default = self.nixosModules.ldk-server;
36-
ldk-server = import ./nix/module.nix;
50+
ldk-server =
51+
{ lib, pkgs, ... }:
52+
{
53+
imports = [ ./nix/module.nix ];
54+
services.ldk-server.package =
55+
lib.mkDefault
56+
self.packages.${pkgs.stdenv.hostPlatform.system}.ldk-server;
57+
};
3758
};
3859

3960
formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt-tree);

nix/module.nix

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ let
1010
cfg = config.services.ldk-server;
1111
toml = pkgs.formats.toml { };
1212

13+
isAbsolutePath = path: lib.hasPrefix "/" path;
14+
isAbsoluteEnvironmentFile = path: isAbsolutePath (lib.removePrefix "-" path);
15+
1316
instanceType = types.submodule (
1417
{ name, ... }:
1518
{
@@ -42,8 +45,8 @@ let
4245
type = types.nullOr types.str;
4346
default = null;
4447
description = ''
45-
An existing TOML configuration file. This option and settings
46-
cannot be used together.
48+
An existing TOML configuration file with an absolute path. This
49+
option and settings cannot be used together.
4750
'';
4851
};
4952

@@ -53,14 +56,18 @@ let
5356
example = [ "/run/secrets/ldk-server-${name}" ];
5457
description = ''
5558
Files with environment variables for secrets and setting
56-
overrides. The files must use systemd EnvironmentFile syntax.
59+
overrides. Each file must have an absolute path and use systemd
60+
EnvironmentFile syntax. Prefix a path with - to make it optional.
5761
'';
5862
};
5963

6064
dataDir = mkOption {
6165
type = types.str;
6266
default = "/var/lib/ldk-server/${name}";
63-
description = "The directory that stores data for this instance.";
67+
description = ''
68+
The absolute path of the directory that stores data for this
69+
instance.
70+
'';
6471
};
6572

6673
user = mkOption {
@@ -155,6 +162,27 @@ in
155162
cannot be used together
156163
'';
157164
}
165+
{
166+
assertion = isAbsolutePath instance.dataDir;
167+
message = ''
168+
services.ldk-server.instances.${name}.dataDir must be an
169+
absolute path
170+
'';
171+
}
172+
{
173+
assertion = instance.configFile == null || isAbsolutePath instance.configFile;
174+
message = ''
175+
services.ldk-server.instances.${name}.configFile must be an
176+
absolute path
177+
'';
178+
}
179+
{
180+
assertion = lib.all isAbsoluteEnvironmentFile instance.environmentFiles;
181+
message = ''
182+
services.ldk-server.instances.${name}.environmentFiles must
183+
contain absolute paths
184+
'';
185+
}
158186
{
159187
assertion = !instance.openFirewall || instance.lightningPort != null || instance.grpcPort != null;
160188
message = ''

nix/tests/module.nix

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
{ pkgs, ldkServerModule }:
2+
3+
let
4+
fakeServer = pkgs.writeShellApplication {
5+
name = "ldk-server";
6+
runtimeInputs = [
7+
pkgs.coreutils
8+
pkgs.systemd
9+
];
10+
text = ''
11+
config_file="$1"
12+
test "$2" = "--storage-dir-path"
13+
data_dir="$3"
14+
15+
cp --remove-destination "$config_file" "$data_dir/observed-config.toml"
16+
printf '%s\n' "$@" > "$data_dir/observed-arguments"
17+
exec systemd-notify --ready --exec ';' sleep infinity
18+
'';
19+
};
20+
in
21+
{
22+
name = "ldk-server-module";
23+
24+
nodes.machine = {
25+
imports = [ ldkServerModule ];
26+
27+
services.ldk-server = {
28+
package = fakeServer;
29+
instances = {
30+
mainnet = {
31+
enable = true;
32+
environmentFiles = [ "-/run/secrets/ldk-server-mainnet" ];
33+
settings.node.network = "bitcoin";
34+
};
35+
signet = {
36+
enable = true;
37+
dataDir = "/var/lib/ldk-server-signet-test";
38+
settings.node.network = "signet";
39+
};
40+
};
41+
};
42+
};
43+
44+
testScript = ''
45+
machine.wait_for_unit("ldk-server-mainnet.service")
46+
machine.wait_for_unit("ldk-server-signet.service")
47+
48+
machine.succeed(
49+
"test $(stat -c '%U:%G:%a' /var/lib/ldk-server/mainnet) = "
50+
"ldk-server-mainnet:ldk-server-mainnet:750"
51+
)
52+
machine.succeed(
53+
"test $(stat -c '%U:%G:%a' /var/lib/ldk-server-signet-test) = "
54+
"ldk-server-signet:ldk-server-signet:750"
55+
)
56+
57+
machine.succeed(
58+
"grep -F 'network = \"bitcoin\"' "
59+
"/var/lib/ldk-server/mainnet/observed-config.toml"
60+
)
61+
machine.succeed(
62+
"grep -F 'dir_path = \"/var/lib/ldk-server/mainnet\"' "
63+
"/var/lib/ldk-server/mainnet/observed-config.toml"
64+
)
65+
machine.succeed(
66+
"grep -F 'network = \"signet\"' "
67+
"/var/lib/ldk-server-signet-test/observed-config.toml"
68+
)
69+
machine.succeed(
70+
"grep -F 'dir_path = \"/var/lib/ldk-server-signet-test\"' "
71+
"/var/lib/ldk-server-signet-test/observed-config.toml"
72+
)
73+
74+
machine.succeed("systemctl stop ldk-server-mainnet.service")
75+
machine.fail("systemctl is-active --quiet ldk-server-mainnet.service")
76+
machine.succeed("systemctl is-active --quiet ldk-server-signet.service")
77+
'';
78+
}

0 commit comments

Comments
 (0)