= macOS Service (launchd)

This option installs OliveTin as a launchd service, so it runs in the background and starts automatically. This is the macOS equivalent of running OliveTin as a Linux systemd service or a xref:install/windows_service.adoc[Windows service]. If you just want to run OliveTin as a regular application, follow the xref:install/macos.adoc[macOS install] instructions instead.

Before continuing, complete the xref:install/macos.adoc[macOS install] steps (download, extract, and confirm Gatekeeper allows the binary) and confirm OliveTin starts correctly by running `./OliveTin`.

== Choose LaunchAgent or LaunchDaemon

launchd offers two ways to run a background service:

* *LaunchAgent* - runs as your user and starts when you log in. No root required. Best for a desktop Mac.
* *LaunchDaemon* - runs as root and starts at boot, before any user logs in. Best for a headless, always-on Mac.

Follow one complete flow below. Both use the same plist structure; only the install locations and `launchctl` domain differ.

== Service definition

Create a file named `app.olivetin.olivetin.plist` with the contents below.

[IMPORTANT]
launchd does *not* expand `~`, so every path in the plist must be absolute. Replace `YOUR_USERNAME` with the output of `whoami` when using the LaunchAgent paths.

[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>app.olivetin.olivetin</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/YOUR_USERNAME/Library/Application Support/OliveTin/OliveTin</string>
        <string>-configdir</string>
        <string>/Users/YOUR_USERNAME/Library/Application Support/OliveTin</string>
    </array>

    <key>WorkingDirectory</key>
    <string>/Users/YOUR_USERNAME/Library/Application Support/OliveTin</string>

    <key>KeepAlive</key>
    <true/>

    <key>RunAtLoad</key>
    <true/>

    <key>StandardOutPath</key>
    <string>/Users/YOUR_USERNAME/Library/Logs/OliveTin/olivetin.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/YOUR_USERNAME/Library/Logs/OliveTin/olivetin.log</string>
</dict>
</plist>
----

For a LaunchDaemon, use the same keys but substitute the paths shown in the table:

[cols="1,1,1"]
|===
| Plist entry | LaunchAgent | LaunchDaemon

| Binary (`ProgramArguments[0]`)
| `/Users/YOUR_USERNAME/Library/Application Support/OliveTin/OliveTin`
| `/usr/local/bin/OliveTin`

| `-configdir` and `WorkingDirectory`
| `/Users/YOUR_USERNAME/Library/Application Support/OliveTin`
| `/usr/local/etc/OliveTin`

| `StandardOutPath` / `StandardErrorPath`
| `/Users/YOUR_USERNAME/Library/Logs/OliveTin/olivetin.log`
| `/usr/local/var/log/olivetin.log`
|===

`WorkingDirectory` makes the relative `webui` and `var` folders resolve inside the config directory, `KeepAlive` restarts OliveTin if it exits (like systemd's `Restart=always`), and `RunAtLoad` starts it as soon as the service is loaded.

[NOTE]
OliveTin looks for `config.yaml` in the directory given by the `-configdir` flag. The plist passes `-configdir` explicitly so the service does not depend on the process working directory alone.

[NOTE]
`launchctl bootstrap`/`bootout` replace the deprecated `launchctl load`/`unload`.

== LaunchAgent (per-user)

=== Install the files

Run these from the extracted archive directory:

[source,shell]
----
# Create the application folder and a place for logs
mkdir -p ~/Library/Application\ Support/OliveTin/var
mkdir -p ~/Library/Logs/OliveTin

# Copy in the binary, your config, and the bundled web UI
cp OliveTin   ~/Library/Application\ Support/OliveTin/
cp config.yaml ~/Library/Application\ Support/OliveTin/
cp -R webui   ~/Library/Application\ Support/OliveTin/
----

This gives you the following layout, all owned by your user:

[source]
----
~/Library/Application Support/OliveTin/
├── OliveTin          # the binary
├── config.yaml       # your configuration
├── webui/            # the web interface assets (shipped in the archive)
└── var/              # runtime data OliveTin writes (logs, etc.)

~/Library/Logs/OliveTin/olivetin.log   # service stdout/stderr
----

=== Register and start

[source,shell]
----
cp app.olivetin.olivetin.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/app.olivetin.olivetin.plist
----

=== Verify

Open http://localhost:1337 in a browser. If the page does not load, check the service log:

[source,shell]
----
tail -f ~/Library/Logs/OliveTin/olivetin.log
----

=== Stop and disable

[source,shell]
----
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/app.olivetin.olivetin.plist
----

=== Restart after a change

After editing `config.yaml` or replacing the binary, restart the service so the change takes effect:

[source,shell]
----
launchctl kickstart -k gui/$(id -u)/app.olivetin.olivetin
----

If you changed the *plist* itself, `kickstart` is not enough - boot the service out and back in so launchd re-reads it (`bootstrap` errors if the service is still loaded):

[source,shell]
----
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/app.olivetin.olivetin.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/app.olivetin.olivetin.plist
----

== LaunchDaemon (system-wide)

=== Install the files

Run these from the extracted archive directory:

[source,shell]
----
sudo mkdir -p /usr/local/bin /usr/local/etc/OliveTin/var /usr/local/var/log

sudo cp OliveTin   /usr/local/bin/OliveTin
sudo cp config.yaml /usr/local/etc/OliveTin/
sudo cp -R webui   /usr/local/etc/OliveTin/
----

This gives you the following layout:

[source]
----
/usr/local/bin/OliveTin
/usr/local/etc/OliveTin/
├── config.yaml
├── webui/
└── var/

/usr/local/var/log/olivetin.log   # service stdout/stderr
----

=== Register and start

[source,shell]
----
sudo cp app.olivetin.olivetin.plist /Library/LaunchDaemons/
sudo chown root:wheel /Library/LaunchDaemons/app.olivetin.olivetin.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/app.olivetin.olivetin.plist
----

=== Verify

Open http://localhost:1337 in a browser. If the page does not load, check the service log:

[source,shell]
----
tail -f /usr/local/var/log/olivetin.log
----

=== Stop and disable

[source,shell]
----
sudo launchctl bootout system /Library/LaunchDaemons/app.olivetin.olivetin.plist
----

=== Restart after a change

After editing `config.yaml` or replacing the binary, restart the service so the change takes effect:

[source,shell]
----
sudo launchctl kickstart -k system/app.olivetin.olivetin
----

If you changed the *plist* itself, `kickstart` is not enough - boot the service out and back in so launchd re-reads it (`bootstrap` errors if the service is still loaded):

[source,shell]
----
sudo launchctl bootout system /Library/LaunchDaemons/app.olivetin.olivetin.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/app.olivetin.olivetin.plist
----

include::partial$install/post_generic.adoc[]
