8 cfg = config.services.anki-sync-server;
9 name = "anki-sync-server";
10 specEscape = replaceStrings ["%"] ["%%"];
12 lists.imap1 (i: user: {
17 usersWithIndexesFile = filter (x: x.user.passwordFile != null) usersWithIndexes;
18 usersWithIndexesNoFile = filter (x: x.user.passwordFile == null && x.user.password != null) usersWithIndexes;
19 anki-sync-server-run = pkgs.writeShellScriptBin "anki-sync-server-run" ''
20 # When services.anki-sync-server.users.passwordFile is set,
21 # each password file is passed as a systemd credential, which is mounted in
22 # a file system exposed to the service. Here we read the passwords from
23 # the credential files to pass them as environment variables to the Anki
28 (x: ''export SYNC_USER${toString x.i}=${escapeShellArg x.user.username}:"''$(cat "''${CREDENTIALS_DIRECTORY}/"${escapeShellArg x.user.username})"'')
31 # For users where services.anki-sync-server.users.password isn't set,
32 # export passwords in environment variables in plaintext.
36 (x: ''export SYNC_USER${toString x.i}=${escapeShellArg x.user.username}:${escapeShellArg x.user.password}'')
37 usersWithIndexesNoFile
39 exec ${cfg.package}/bin/anki-sync-server
42 options.services.anki-sync-server = {
43 enable = mkEnableOption "anki-sync-server";
45 package = mkPackageOption pkgs "anki-sync-server" { };
51 IP address anki-sync-server listens to.
52 Note host names are not resolved.
59 description = "Port number anki-sync-server listens to.";
62 baseDirectory = mkOption {
65 description = "Base directory where user(s) synchronized data will be stored.";
69 openFirewall = mkOption {
72 description = "Whether to open the firewall for the specified port.";
81 description = "User name accepted by anki-sync-server.";
87 Password accepted by anki-sync-server for the associated username.
88 **WARNING**: This option is **not secure**. This password will
89 be stored in *plaintext* and will be visible to *all users*.
90 See {option}`services.anki-sync-server.users.passwordFile` for
94 passwordFile = mkOption {
98 File containing the password accepted by anki-sync-server for
99 the associated username. Make sure to make readable only by
105 description = "List of user-password pairs to provide to the sync server.";
109 config = mkIf cfg.enable {
112 assertion = (builtins.length usersWithIndexesFile) + (builtins.length usersWithIndexesNoFile) > 0;
113 message = "At least one username-password pair must be set.";
116 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.port];
118 systemd.services.anki-sync-server = {
119 description = "anki-sync-server: Anki sync server built into Anki";
120 after = ["network.target"];
121 wantedBy = ["multi-user.target"];
122 path = [cfg.package];
124 SYNC_BASE = cfg.baseDirectory;
125 SYNC_HOST = specEscape cfg.address;
126 SYNC_PORT = toString cfg.port;
132 StateDirectory = name;
133 ExecStart = "${anki-sync-server-run}/bin/anki-sync-server-run";
137 (x: "${specEscape x.user.username}:${specEscape (toString x.user.passwordFile)}")
138 usersWithIndexesFile;
144 maintainers = with maintainers; [telotortium];
145 doc = ./anki-sync-server.md;