Skip to content

feat: add --run-as-service for background daemon mode (linux/macos only)#72

Open
mforce wants to merge 1 commit into
siddsachar:mainfrom
mforce:feature/run-as-service
Open

feat: add --run-as-service for background daemon mode (linux/macos only)#72
mforce wants to merge 1 commit into
siddsachar:mainfrom
mforce:feature/run-as-service

Conversation

@mforce
Copy link
Copy Markdown

@mforce mforce commented May 8, 2026

Summary

Adds a --run-as-service flag (and friends) to thoth so the app can run detached from the terminal — closing the shell no longer shuts it down. Implemented as a small service module that handles POSIX daemonization, PID-file management, and systemd unit generation.

New flags

Flag Purpose
--run-as-service Double-fork to detach. Writes PID to $THOTH_DATA_DIR/service.pid, redirects stdout/stderr to $THOTH_DATA_DIR/service.log. Forces --server --no-tray --no-open --no-splash.
--service-stop SIGTERM the daemon (then SIGKILL after 10s).
--service-status Print running/stopped + PID.
--service-restart Stop existing daemon, then start a new one.
--install-systemd-service Write ~/.config/systemd/user/thoth.service with Type=simple — recommended long-term setup.
--pid-file / --service-log Override default paths.

Notes

  • bin/thoth already forwards extra args (exec "$PYTHON" launcher.py "$@"), so no installer change was required.
  • Windows is intentionally not supported by the hand-rolled daemon; on Windows the flag prints a message pointing users to Task Scheduler / NSSM. macOS works the same as Linux.
  • Defaults respect THOTH_DATA_DIR (matches the existing convention in logging_config.py).

Usage

thoth --run-as-service        # start in background
thoth --service-status        # check
thoth --service-stop          # stop
thoth --install-systemd-service && \
  systemctl --user daemon-reload && \
  systemctl --user enable --now thoth.service

Test plan

  • Unit tests in tests/test_service.py cover read/write/remove PID, is_alive, status messages, stop on stale PID, stop on a real child process, systemd unit generation, default-path resolution, and CLI routing for --service-status / --service-stop / --install-systemd-service.
  • End-to-end manual smoke test: forked a real daemon via service.daemonize, verified the PID file is written, is_alive reports true, stdout is captured to the log file, and stop_service cleanly terminates and removes the PID file.
  • Try on macOS (only Linux verified locally).
  • Confirm --install-systemd-service produces a working unit when used end-to-end on a system with systemctl --user.

@mforce mforce requested a review from siddsachar as a code owner May 8, 2026 04:47
@mforce mforce marked this pull request as draft May 8, 2026 05:12
@mforce mforce marked this pull request as ready for review May 8, 2026 05:14
@mforce mforce changed the title feat: add --run-as-service for background daemon mode feat: add --run-as-service for background daemon mode (linux/macos only) May 8, 2026
Adds a `service` module and wires `launcher.main()` to support running
Thoth detached from the terminal so closing the shell does not shut the
app down.

New flags:
  --run-as-service        POSIX double-fork; writes PID to
                          $THOTH_DATA_DIR/service.pid and redirects
                          stdout/stderr to $THOTH_DATA_DIR/service.log.
                          Forces --server --no-tray --no-open --no-splash.
  --service-stop          SIGTERM (then SIGKILL after 10s) the daemon.
  --service-status        Print running/stopped + PID.
  --service-restart       Stop existing daemon, then start a new one.
  --install-systemd-service
                          Write ~/.config/systemd/user/thoth.service
                          (Type=simple) for a cleaner long-term setup.
  --pid-file / --service-log
                          Override default paths.

Windows is not supported by --run-as-service; users get a message
pointing them to Task Scheduler / NSSM. The existing bin/thoth wrapper
already forwards extra args, so no installer change is needed.
@mforce mforce force-pushed the feature/run-as-service branch from 7c7347d to 89ca085 Compare May 8, 2026 05:39
Copy link
Copy Markdown
Owner

@siddsachar siddsachar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. The POSIX daemon approach looks promising: I verified tests/test_service.py in WSL and did a real service.daemonize smoke where the PID file/log/stop path worked.

Before merge, could you please address two platform-safety issues?

  1. Windows/non-POSIX service management should be hard-gated before calling PID liveness logic. Locally on Windows, tests/test_service.py::test_is_alive_for_self_and_invalid raises KeyboardInterrupt because os.kill(pid, 0) is not a safe liveness probe there. Since launcher.py exposes --service-status / --service-stop on all platforms, those commands can currently reach unsafe logic even though daemon mode is Linux/macOS only.

  2. --install-systemd-service should be Linux-only. Right now install_systemd_unit() rejects Windows but would write ~/.config/systemd/user/thoth.service on macOS, which is not useful and contradicts the systemd-only behavior.

I can take care of wiring tests/test_service.py into our CI/release focused test commands once those feature-level fixes are in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants