]> git.giacomolli.uk - onechat/commitdiff
Package a systemd user service
authorAnderson A. Giacomolli <redacted>
Mon, 9 Nov 2026 01:51:50 +0000 (22:51 -0300)
committerAnderson A. Giacomolli <redacted>
Mon, 9 Nov 2026 01:51:50 +0000 (22:51 -0300)
The flake package supplies a user unit that runs its own daemon binary
at login, restarts failures after five seconds, and sends output to the
journal. SIGTERM starts cleanup, with two minutes before a forced stop.
The unit keeps the rights of the user and a private file creation mask.

A unit in the existing package is the smallest flake interface for this
service. It needs no extra module interface or dependency and leaves the
data directory and tool environment in a standard user drop-in.

The README covers the data copy with the daemon stopped, the shared
client data setting, the package result link, and service controls. A
flake check verifies the built unit without a running user manager.

Issue: 99a5e07

README.rst
flake.nix
nix/onechat.service [new file with mode: 0644]
nix/package.nix
nix/user-service-check.nix [new file with mode: 0644]

index 7930b0e1deac1f4b9c146e9646c1bb858160aa9a..9fbf6ddd89ff71d19fc762a77c4148425e7d9582 100644 (file)
@@ -129,6 +129,80 @@ prompt cache key comes from it.
 how their files are organized, and how they want work done. The prompt
 ends with them, and a change applies at the next call.
 
+**************
+ User service
+**************
+
+The Nix package includes ``share/systemd/user/onechat.service``. It runs
+``onechat daemon`` from that package, starts at login when enabled, and
+restarts 5 seconds after a failure. A stop sends SIGTERM to the daemon
+and allows up to 120 seconds for cleanup. Its output goes to the user
+journal. The service has the rights of the user, as the terminal daemon
+does.
+
+Build a package with a separate result link. Keep this link so that Nix
+does not collect the package while the service uses it:
+
+.. code:: console
+
+   nix build --out-link result-service
+   systemctl --user link --force "$(realpath result-service/share/systemd/user/onechat.service)"
+   systemctl --user edit onechat.service
+
+In the editor, set the data directory with this drop-in. Replace
+``DATA_DIR`` with an absolute path to the data directory. Set the same
+``ONECHAT_DIR`` for each terminal client. Without a drop-in, the data
+directory rules above apply to the environment of the user manager, not
+to the development shell.
+
+.. code:: ini
+
+   [Service]
+   Environment="ONECHAT_DIR=DATA_DIR"
+
+The service does not read the environment of an open terminal. If the
+agent needs tools from a shell, set ``Environment="PATH=TOOLS_PATH"`` in
+the same drop-in, with ``TOOLS_PATH`` replaced by the complete search
+path, including bash. For the API endpoint, use
+``EnvironmentFile=ENV_FILE``, with ``ENV_FILE`` replaced by an absolute
+path to a mode 0600 file that sets ``OPENAI_API_KEY``. Keep secrets out
+of the unit and Nix sources.
+
+Use the existing data directory, or copy the development data before the
+first service start. For a copy, first stop the terminal daemon with
+Ctrl-C and wait for it to exit. Replace ``DATA_DIR`` below with a new,
+empty destination. If the development shell uses a custom
+``ONECHAT_DIR``, replace ``.devenv/onechat`` with that source. Copy the
+whole directory so that ``auth.json``, ``telegram.json``,
+``config.json``, ``agent/``, and ``whatsapp/`` stay together, including
+the WhatsApp session and its database files. The copy preserves the file
+modes and the source as a backup. Do not copy or use the same data from
+two running daemons.
+
+.. code:: console
+
+   install -d -m 0700 DATA_DIR
+   cp -a .devenv/onechat/. DATA_DIR/
+
+After the terminal daemon exits and the data is ready, enable and start
+the service:
+
+.. code:: console
+
+   systemctl --user daemon-reload
+   systemctl --user enable --now onechat.service
+   systemctl --user status onechat.service
+   ONECHAT_DIR=DATA_DIR ./result-service/bin/onechat chat
+   journalctl --user -u onechat.service -f
+
+The service starts with the user manager at login, not at boot without a
+login. To stop it, or also remove its start at login:
+
+.. code:: console
+
+   systemctl --user stop onechat.service
+   systemctl --user disable onechat.service
+
 **********
  Telegram
 **********
index f21a609f6fe3d0fc47a323e66a6b21c305790bc5..c2ca691ae7851b43ccf3c766446cf9092735615a 100644 (file)
--- a/flake.nix
+++ b/flake.nix
       checks = forEachSystem (system: {
         package = self.packages.${system}.default;
         replay = import ./nix/replay.nix { onechat = self.packages.${system}.default; };
+        user-service = import ./nix/user-service-check.nix {
+          pkgs = nixpkgs.legacyPackages.${system};
+          onechat = self.packages.${system}.default;
+        };
       });
 
       formatter = forEachSystem (system: nixpkgs.legacyPackages.${system}.nixfmt);
diff --git a/nix/onechat.service b/nix/onechat.service
new file mode 100644 (file)
index 0000000..27047dc
--- /dev/null
@@ -0,0 +1,18 @@
+[Unit]
+Description=OneChat daemon
+
+[Service]
+Type=exec
+ExecStart=@onechat@ daemon
+WorkingDirectory=%h
+Restart=on-failure
+RestartSec=5s
+KillSignal=SIGTERM
+KillMode=mixed
+TimeoutStopSec=120s
+UMask=0077
+StandardOutput=journal
+StandardError=journal
+
+[Install]
+WantedBy=default.target
index 67a11a603805f23f39725e71eecf25b7334142a8..729cf5347375056d2b6b91e13ac29ac713adf86d 100644 (file)
@@ -14,6 +14,12 @@ buildGoModule {
   # The flake check runs the full replay.
   checkFlags = [ "-short" ];
 
+  postInstall = ''
+    mkdir -p "$out/share/systemd/user"
+    substitute ${./onechat.service} "$out/share/systemd/user/onechat.service" \
+      --subst-var-by onechat "$out/bin/onechat"
+  '';
+
   doInstallCheck = true;
   installCheckPhase = ''
     runHook preInstallCheck
diff --git a/nix/user-service-check.nix b/nix/user-service-check.nix
new file mode 100644 (file)
index 0000000..38ab9ca
--- /dev/null
@@ -0,0 +1,7 @@
+{ pkgs, onechat }:
+pkgs.runCommand "onechat-user-service-check" { nativeBuildInputs = [ pkgs.systemd ]; } ''
+  XDG_RUNTIME_DIR="$TMPDIR" \
+    SYSTEMD_UNIT_PATH="${pkgs.systemd}/example/systemd/user" \
+    systemd-analyze --user --man=no verify ${onechat}/share/systemd/user/onechat.service
+  touch "$out"
+''