# How the Summarize Daemon Handles Autostart on macOS, Linux, and Windows

> Learn how the Summarize daemon autostarts on macOS, Linux, and Windows. Discover its cross-platform service configuration using launchd, systemd, and schtasks.

- Repository: [Peter Steinberger/summarize](https://github.com/steipete/summarize)
- Tags: internals
- Published: 2026-02-19

---

**The steipete/summarize daemon implements cross-platform autostart by generating native service configurations—LaunchAgent plists for macOS, systemd units for Linux, and Task Scheduler jobs for Windows—and manages their lifecycle through dedicated TypeScript modules that interface with launchctl, systemctl, and schtasks.**

The steipete/summarize repository provides a background daemon that automatically starts when users log in, eliminating manual service management across operating systems. By abstracting **daemon autostart** mechanisms into platform-specific modules, the codebase ensures consistent behavior while respecting each platform's native service architecture. The implementation targets user-level services rather than system-wide daemons, ensuring installation succeeds without administrative privileges.

## macOS LaunchAgent Integration (launchd)

The macOS implementation in [`src/daemon/launchd.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/launchd.ts) leverages **launchd** to manage the daemon as a user LaunchAgent. This approach ensures the service starts automatically upon user login without requiring system-wide installation privileges. The module handles the complete lifecycle from plist generation to service bootstrap using native macOS tooling.

### Building the Plist Configuration

The `buildLaunchAgentPlist` function constructs a property list file defining service parameters including program arguments, working directory, and log redirection. Paths are resolved via `resolveDaemonLogPaths`, directing stdout and stderr to `~/.summarize/logs/daemon.log`. The resulting plist targets the user domain through `resolveLaunchctlDomains`, ensuring proper GUI session integration.

### Installation and Bootstrap Flow

The `installLaunchAgent` function orchestrates the installation through three critical operations:

1. Resolves log paths and validates the environment using `resolveDaemonLogPaths`
2. Writes the plist to `~/Library/LaunchAgents/` after removing any stale configurations
3. Executes `launchctl bootstrap` followed by `kickstart` to load and activate the agent in the appropriate user domain

## Linux systemd User Service

For Linux environments, [`src/daemon/systemd.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/systemd.ts) implements **systemd** user-service integration, allowing the daemon to run within the user's session context. The module verifies systemd availability before attempting installation and manages the service through standard systemctl commands.

### Unit File Generation and Validation

Before installation, `assertSystemdAvailable` verifies that `systemctl --user status` executes successfully, confirming the systemd user instance is functional. The `buildSystemdUnit` function generates a service unit file containing the `ExecStart` directive, environment variables, and working directory specifications, with output redirected to `~/.summarize/logs/daemon.log`.

### Service Lifecycle Management

The `installSystemdService` function writes the unit to `~/.config/systemd/user/` and executes a reload-enable-start sequence:

- `systemctl --user daemon-reload` refreshes the systemd configuration
- `systemctl --user enable` marks the service for autostart
- `systemctl --user restart` immediately activates the daemon

## Windows Task Scheduler Integration (schtasks)

The Windows implementation in [`src/daemon/schtasks.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/schtasks.ts) utilizes the **Task Scheduler** (`schtasks`) to register a logon-triggered task. This approach provides native Windows service management without requiring custom service wrappers or registry modifications.

### Batch Script Preparation

Unlike Unix platforms that pass arguments directly to the executable, Windows requires an intermediary script. The `buildTaskScript` function generates `daemon.cmd` containing the execution command with working directory navigation via `cd /d`. This script is written to `%USERPROFILE%\.summarize\daemon.cmd` and serves as the task's action target.

### Task Registration and Execution

The `installScheduledTask` function first validates Task Scheduler availability through `assertSchtasksAvailable`. It then registers a task named `DAEMON_WINDOWS_TASK_NAME` configured to trigger on user logon, immediately firing the task via the `/Run` parameter to start the daemon without requiring a logout cycle.

## Cross-Platform CLI Abstraction

The daemon exposes these platform-specific implementations through a unified interface in [`src/daemon/cli.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/cli.ts). Based on `process.platform`, the CLI delegates to the appropriate installer, providing consistent commands regardless of the underlying operating system.

- **macOS**: `installLaunchAgent` from [`src/daemon/launchd.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/launchd.ts)
- **Linux**: `installSystemdService` from [`src/daemon/systemd.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/systemd.ts)
- **Windows**: `installScheduledTask` from [`src/daemon/schtasks.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/schtasks.ts)

### Installing via Command Line

Users can install and enable autostart with a single command:

```bash
summarize daemon install

```

### Programmatic API Usage

Developers can invoke platform installers directly for custom integration scenarios.

**macOS implementation:**

```typescript
import { installLaunchAgent } from "./daemon/launchd.js";
import { stdout } from "process";

await installLaunchAgent({
  env: process.env,
  stdout,
  programArguments: ["summarize", "daemon", "run"],
  workingDirectory: "/Users/alice",
});

```

**Linux implementation:**

```typescript
import { installSystemdService } from "./daemon/systemd.js";
import { stdout } from "process";

await installSystemdService({
  env: process.env,
  stdout,
  programArguments: ["summarize", "daemon", "run"],
  workingDirectory: "/home/alice",
});

```

**Windows implementation:**

```typescript
import { installScheduledTask } from "./daemon/schtasks.js";
import { stdout } from "process";

await installScheduledTask({
  env: process.env,
  stdout,
  programArguments: ["summarize", "daemon", "run"],
  workingDirectory: "C:\\Users\\Alice",
});

```

## Summary

- The steipete/summarize daemon implements **daemon autostart** through native platform service managers: launchd on macOS, systemd on Linux, and Task Scheduler on Windows.
- Each platform module handles configuration generation, installation to user-specific directories (`~/Library/LaunchAgents/`, `~/.config/systemd/user/`, and `%USERPROFILE%\.summarize\`), and service activation through standard command-line tools.
- The implementation provides complete lifecycle management through paired uninstall, restart, and status-check functions (`uninstallLaunchAgent`, `restartSystemdService`, `isScheduledTaskInstalled`, etc.).
- The unified CLI in [`src/daemon/cli.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/cli.ts) automatically selects the correct backend based on `process.platform`, while programmatic APIs allow direct access to platform-specific installers.

## Frequently Asked Questions

### How does the daemon handle log file redirection across platforms?

All three platforms redirect stdout and stderr to `~/.summarize/logs/daemon.log`. On macOS, the `buildLaunchAgentPlist` function embeds `StandardOutPath` and `StandardErrorPath` keys in the plist. On Linux, the systemd unit file generated by `buildSystemdUnit` includes shell redirection in the `ExecStart` directive. Windows achieves this through output redirection operators within the `daemon.cmd` batch script created by `buildTaskScript`.

### Can the daemon autostart without administrator privileges?

Yes. The implementation specifically targets user-level service managers rather than system-level services. On macOS, it installs to `~/Library/LaunchAgents/` rather than `/Library/LaunchDaemons/`. On Linux, it uses `systemctl --user` commands targeting `~/.config/systemd/user/`. On Windows, it registers tasks in the user context via `schtasks` without requiring elevation, ensuring the daemon starts only when the specific user logs in.

### What happens if the service configuration already exists?

Each installer includes idempotent behavior to handle existing configurations. The macOS implementation removes stale plist copies before writing new configurations. The Linux module overwrites existing unit files and executes `daemon-reload` to ensure systemd recognizes changes. The Windows implementation updates or recreates the task named `DAEMON_WINDOWS_TASK_NAME` to ensure configuration consistency.

### How does the CLI determine which autostart mechanism to use?

The [`src/daemon/cli.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/cli.ts) module inspects `process.platform` to select the appropriate backend. When `process.platform` equals `darwin`, it imports and calls functions from [`src/daemon/launchd.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/launchd.ts). For `linux`, it uses [`src/daemon/systemd.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/systemd.ts). For `win32`, it delegates to [`src/daemon/schtasks.ts`](https://github.com/steipete/summarize/blob/main/src/daemon/schtasks.ts). This platform detection occurs at runtime, allowing the same CLI binary to operate correctly across different operating systems without manual configuration.