Migration This is my new public git source viewer. Source coming soon. Please contact with concerns/bugs. Contact me

users/ryan/modules/aria2/default.nix

3c1f45ed7ea27458db27a2af96ef6ec1392dbc15 · 6.5 KB · 185 lines raw

1 # users/ryan/modules/aria2/default.nix
2 #
3 # aria2c RPC daemon + Zen hand-off via baptistecdr's "Aria2 Integration"
4 # (AMO slug aria2-extension). The RPC secret is generated once at activation
5 # into $XDG_STATE_HOME/aria2/rpc-secret (0600) and never touches the repo or
6 # the store; the same activation splices it into both aria2's runtime conf and
7 # the extension's storage.sync row.
8 { config, lib, pkgs, ... }:
9 let
10 cfg = config.ryan.aria2;
11 zen = config.programs.zen-browser;
12 inherit (pkgs.stdenv.hostPlatform) isDarwin isLinux;
13
14 stateDir = "${config.xdg.stateHome}/aria2";
15 secretFile = "${stateDir}/rpc-secret";
16 runtimeConf = "${stateDir}/aria2.conf";
17 sessionFile = "${stateDir}/session";
18
19 extId = "baptistecdr@users.noreply.github.com";
20 # Fixed so captureServer can point at it and re-seeding is idempotent.
21 serverUuid = "6f1c2b7e-0a43-4d8e-9c55-3b2a1e0d9f10";
22
23 # Secret-free half of the config; rpc-secret is appended at activation.
24 baseConf = (pkgs.formats.keyValue { }).generate "aria2-base.conf" ({
25 enable-rpc = true;
26 rpc-listen-all = false;
27 rpc-listen-port = cfg.rpcPort;
28 dir = cfg.downloadDir;
29 input-file = sessionFile;
30 save-session = sessionFile;
31 save-session-interval = 60;
32 continue = true;
33 max-connection-per-server = 8;
34 split = 8;
35 min-split-size = "1M";
36 file-allocation = if isDarwin then "none" else "falloc";
37 log = "${stateDir}/aria2.log";
38 log-level = "warn";
39 } // lib.optionalAttrs isLinux {
40 #ca-certificate = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
41 });
42
43 # Extension state as stored by src/models/extension-options.ts: one
44 # storage.sync key, "options", holding a *stringified* JSON blob.
45 extOptions = pkgs.writeText "aria2-ext-options.json" (builtins.toJSON {
46 servers.${serverUuid} = {
47 uuid = serverUuid;
48 name = "Local aria2";
49 secure = false;
50 host = "127.0.0.1";
51 port = cfg.rpcPort;
52 path = "/jsonrpc";
53 secret = ""; # filled at activation
54 rpcParameters = { };
55 };
56 captureServer = serverUuid;
57 captureDownloads = true;
58 minFileSizeInBytes = 0;
59 excludedProtocols = [ "blob" "data" ];
60 excludedSites = [ ];
61 excludedFileTypes = [ ];
62 useCompleteFilePath = false;
63 notifyUrlIsAdded = true;
64 notifyFileIsAdded = false;
65 notifyErrorOccurs = true;
66 });
67
68 daemon = pkgs.writeShellApplication {
69 name = "aria2-daemon";
70 runtimeInputs = [ pkgs.aria2 ];
71 text = ''exec aria2c --conf-path="${runtimeConf}" "$@"'';
72 };
73 in
74 {
75 options.ryan.aria2 = {
76 enable = lib.mkEnableOption "aria2 RPC daemon wired into Zen";
77 rpcPort = lib.mkOption { type = lib.types.port; default = 6800; };
78 downloadDir = lib.mkOption {
79 type = lib.types.str;
80 default = "${config.home.homeDirectory}/Downloads";
81 };
82 daemon = lib.mkOption {
83 type = lib.types.package;
84 readOnly = true;
85 default = daemon;
86 description = "Wrapper that runs aria2c against the runtime conf.";
87 };
88 };
89
90 config = lib.mkIf cfg.enable (lib.mkMerge [
91 {
92 home.packages = [ pkgs.aria2 daemon ];
93
94 # Secret + runtime conf. Must precede the launchd/systemd/Shepherd reload and the
95 # Zen fragment, all of which consume what this writes.
96 home.activation.aria2Runtime =
97 lib.hm.dag.entryBetween
98 [ "setupLaunchAgents" "reloadSystemd" "shepherdServices" "zen-browser-default" ]
99 [ "writeBoundary" ] ''
100 if [[ ! -v DRY_RUN ]]; then
101 install -d -m 0700 "${stateDir}"
102 if [ ! -s "${secretFile}" ]; then
103 (umask 077; ${pkgs.openssl}/bin/openssl rand -hex 32 > "${secretFile}")
104 fi
105 [ -e "${sessionFile}" ] || touch "${sessionFile}"
106 tmp="$(mktemp "${stateDir}/.aria2.conf.XXXXXX")"
107 { cat ${baseConf}; printf 'rpc-secret=%s\n' "$(cat "${secretFile}")"; } > "$tmp"
108 chmod 0600 "$tmp"
109 mv -f "$tmp" "${runtimeConf}"
110 fi
111 '';
112 }
113
114 (lib.mkIf config.ryan.zen.enable {
115 ryan.zen.extensions.aria2 = {
116 id = extId;
117 version = "4.15.4";
118 };
119
120 # The extension only reads storage.sync (no storage.managed), so
121 # 3rdparty policy can't configure it. With Sync disabled, storage.sync
122 # is the local webext-storage DB; upsert our row there while Zen is
123 # closed. Declarative-owned: UI edits to this extension get reverted.
124 programs.zen-browser.activationFragments.default = [{
125 requiresLock = true;
126 skipSubject = "aria2 extension config";
127 text = ''
128 db="${zen.profilesPath}/${zen.profiles.default.path}/storage-sync-v2.sqlite"
129 if [ ! -f "$db" ]; then
130 echo "aria2: $db not created yet; start Zen once (extension initialises storage.sync), quit, rebuild."
131 elif [[ ! -v DRY_RUN ]]; then
132 payload="$(mktemp)"
133 ${lib.getExe pkgs.jq} -nc \
134 --arg secret "$(cat "${secretFile}")" \
135 --arg uuid "${serverUuid}" \
136 --slurpfile opts ${extOptions} \
137 '{options: ($opts[0] | .servers[$uuid].secret = $secret | tojson)}' > "$payload"
138 ${lib.getBin pkgs.sqlite}/bin/sqlite3 "$db" <<SQL
139 INSERT INTO storage_sync_data (ext_id, data, sync_change_counter)
140 VALUES ('${extId}', CAST(readfile('$payload') AS TEXT), 1)
141 ON CONFLICT(ext_id) DO UPDATE SET
142 data = excluded.data,
143 sync_change_counter = sync_change_counter + 1;
144 SQL
145 rm -f "$payload"
146 fi
147 '';
148 }];
149 })
150
151 (lib.mkIf isDarwin {
152 launchd.agents.aria2 = {
153 enable = true;
154 config = {
155 ProgramArguments = [ (lib.getExe daemon) ];
156 RunAtLoad = true;
157 KeepAlive = true;
158 ProcessType = "Background";
159 # Not read by aria2; changes the plist whenever the base conf
160 # changes, so setupLaunchAgents reloads the agent.
161 EnvironmentVariables.ARIA2_BASE_CONF = "${baseConf}";
162 };
163 };
164 })
165
166 # Linux: a plain systemd user unit. On Guix, targets.guix translates it
167 # to a Shepherd service; on NixOS / systemd distros it runs as-is.
168 (lib.mkIf isLinux {
169 systemd.user.services.aria2 = {
170 Unit = {
171 Description = "aria2 RPC daemon";
172 Documentation = "man:aria2c(1)";
173 # Unit text changes with the base conf -> sd-switch restarts it.
174 X-Restart-Triggers = [ "${baseConf}" ];
175 };
176 Service = {
177 ExecStart = lib.getExe daemon;
178 Restart = "on-failure";
179 };
180 Install.WantedBy = [ "default.target" ];
181 };
182 })
183
184 ]);
185 }