From: Anderson A. Giacomolli Date: Mon, 9 Nov 2026 01:51:50 +0000 (-0300) Subject: Package a systemd user service X-Git-Url: http://git.giacomolli.uk/onechat/commitdiff_plain/2501e78d10204fac2298db0dee84b3dce379e09b?ds=sidebyside Package a systemd user service 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 --- diff --git a/README.rst b/README.rst index 7930b0e..9fbf6dd 100644 --- a/README.rst +++ b/README.rst @@ -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 ********** diff --git a/flake.nix b/flake.nix index f21a609..c2ca691 100644 --- a/flake.nix +++ b/flake.nix @@ -37,6 +37,10 @@ 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 index 0000000..27047dc --- /dev/null +++ b/nix/onechat.service @@ -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 diff --git a/nix/package.nix b/nix/package.nix index 67a11a6..729cf53 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -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 index 0000000..38ab9ca --- /dev/null +++ b/nix/user-service-check.nix @@ -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" +''