diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index e07fabb348c0..1b944199a66c 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -473,6 +473,7 @@
./services/misc/synergy.nix
./services/misc/sysprof.nix
./services/misc/taskserver
+ ./services/misc/tiddlywiki.nix
./services/misc/tzupdate.nix
./services/misc/uhub.nix
./services/misc/weechat.nix
diff --git a/nixos/modules/services/misc/tiddlywiki.nix b/nixos/modules/services/misc/tiddlywiki.nix
new file mode 100644
index 000000000000..2adc08f6cfed
--- /dev/null
+++ b/nixos/modules/services/misc/tiddlywiki.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.tiddlywiki;
+ listenParams = concatStrings (mapAttrsToList (n: v: " '${n}=${toString v}' ") cfg.listenOptions);
+ exe = "${pkgs.nodePackages.tiddlywiki}/lib/node_modules/.bin/tiddlywiki";
+ name = "tiddlywiki";
+ dataDir = "/var/lib/" + name;
+
+in {
+
+ options.services.tiddlywiki = {
+
+ enable = mkEnableOption "TiddlyWiki nodejs server";
+
+ listenOptions = mkOption {
+ type = types.attrs;
+ default = {};
+ example = {
+ credentials = "../credentials.csv";
+ readers="(authenticated)";
+ port = 3456;
+ };
+ description = ''
+ Parameters passed to --listen command.
+ Refer to
+ for details on supported values.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd = {
+ services.tiddlywiki = {
+ description = "TiddlyWiki nodejs server";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ Type = "simple";
+ Restart = "on-failure";
+ DynamicUser = true;
+ StateDirectory = name;
+ ExecStartPre = "-${exe} ${dataDir} --init server";
+ ExecStart = "${exe} ${dataDir} --listen ${listenParams}";
+ };
+ };
+ };
+ };
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index e84635a3d2ef..e490165ca1ea 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -247,6 +247,7 @@ in
pdns-recursor = handleTest ./pdns-recursor.nix {};
taskserver = handleTest ./taskserver.nix {};
telegraf = handleTest ./telegraf.nix {};
+ tiddlywiki = handleTest ./tiddlywiki.nix {};
tinydns = handleTest ./tinydns.nix {};
tor = handleTest ./tor.nix {};
transmission = handleTest ./transmission.nix {};
diff --git a/nixos/tests/tiddlywiki.nix b/nixos/tests/tiddlywiki.nix
new file mode 100644
index 000000000000..4a2014a4ec91
--- /dev/null
+++ b/nixos/tests/tiddlywiki.nix
@@ -0,0 +1,67 @@
+import ./make-test.nix ({ ... }: {
+ name = "tiddlywiki";
+ nodes = {
+ default = {
+ services.tiddlywiki.enable = true;
+ };
+ configured = {
+ boot.postBootCommands = ''
+ echo "username,password
+ somelogin,somesecret" > /var/lib/wikiusers.csv
+ '';
+ services.tiddlywiki = {
+ enable = true;
+ listenOptions = {
+ port = 3000;
+ credentials="../wikiusers.csv";
+ readers="(authenticated)";
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ startAll;
+
+ subtest "by default works without configuration", sub {
+ $default->waitForUnit("tiddlywiki.service");
+ };
+
+ subtest "by default available on port 8080 without auth", sub {
+ $default->waitForUnit("tiddlywiki.service");
+ $default->waitForOpenPort(8080);
+ $default->succeed("curl --fail 127.0.0.1:8080");
+ };
+
+ subtest "by default creates empty wiki", sub {
+ $default->succeed("test -f /var/lib/tiddlywiki/tiddlywiki.info");
+ };
+
+ subtest "configured on port 3000 with basic auth", sub {
+ $configured->waitForUnit("tiddlywiki.service");
+ $configured->waitForOpenPort(3000);
+ $configured->fail("curl --fail 127.0.0.1:3000");
+ $configured->succeed("curl --fail 127.0.0.1:3000 --user somelogin:somesecret");
+ };
+
+ subtest "configured with different wikifolder", sub {
+ $configured->succeed("test -f /var/lib/tiddlywiki/tiddlywiki.info");
+ };
+
+ subtest "restart preserves changes", sub {
+ # given running wiki
+ $default->waitForUnit("tiddlywiki.service");
+ # with some changes
+ $default->succeed("curl --fail --request PUT --header 'X-Requested-With:TiddlyWiki' --data '{ \"title\": \"title\", \"text\": \"content\" }' --url 127.0.0.1:8080/recipes/default/tiddlers/somepage ");
+ $default->succeed("sleep 2"); # server syncs to filesystem on timer
+
+ # when wiki is cycled
+ $default->systemctl("restart tiddlywiki.service");
+ $default->waitForUnit("tiddlywiki.service");
+ $default->waitForOpenPort(8080);
+
+ # the change is preserved
+ $default->succeed("curl --fail 127.0.0.1:8080/recipes/default/tiddlers/somepage");
+ };
+ '';
+})