All three deployment methods bound 0.0.0.0:8000 with no API key, so following
the README produced a server reachable from any device on the network with no
auth. `check_bind_safety` already refuses to start a non-loopback bind without
a key (so these configs actually failed to start) — this wires the key in so
the documented path yields a *working, authenticated* server.
- docker-compose.yml: require `OPENJARVIS_API_KEY` via `${VAR:?...}` so
`docker compose up` fails fast when unset; added `deploy/docker/.env.example`
(un-ignored in .gitignore).
- systemd: add `EnvironmentFile=/etc/openjarvis/env` (no `-` prefix, so a
missing key file blocks startup rather than exposing an open server).
- launchd: bind `127.0.0.1` by default (the personal-device default — no
network exposure, no key needed) with a documented, commented opt-in to
0.0.0.0 + `OPENJARVIS_API_KEY`. Avoids shipping a usable default credential.
- Docs (docker/systemd/launchd) updated with the key-setup step.
- Tests assert each config can't reintroduce an open server, plus
`check_bind_safety` behavior across loopback/public × key/no-key.
Closes #221
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.7 KiB
launchd Service (macOS)
OpenJarvis includes a launchd property list (plist) for running the API server as a background service on macOS. This provides automatic startup at login, automatic restart if the process exits, and log capture.
Prerequisites
Before installing the service, ensure that OpenJarvis is installed and the jarvis command is available at /usr/local/bin/jarvis. If you installed via uv or pip with a different prefix, adjust the path in the plist accordingly.
git clone https://github.com/open-jarvis/OpenJarvis.git && cd OpenJarvis && uv sync --extra server
which jarvis # Verify the installation path
Also ensure that an inference engine (such as Ollama) is running and accessible on the machine.
Installing the Service
Copy the plist file to ~/Library/LaunchAgents and load it:
cp deploy/launchd/com.openjarvis.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.openjarvis.plist
The service starts immediately (due to RunAtLoad) and will automatically restart at each login.
!!! note "Binds loopback by default"
The plist binds 127.0.0.1 — reachable from this Mac but not the network,
the right default for a personal device, and no API key is needed. To
expose it on your LAN, change the host to 0.0.0.0 and uncomment the
EnvironmentVariables block to set OPENJARVIS_API_KEY
(jarvis auth generate-key); an unauthenticated 0.0.0.0 server refuses
to start.
Verify it is running:
launchctl list | grep openjarvis
You should see a line with the PID and the label com.openjarvis. A 0 in the status column indicates the service is running normally.
Confirm the server is responding:
curl http://localhost:8000/health
Plist Reference
The provided plist file at deploy/launchd/com.openjarvis.plist:
<?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>com.openjarvis</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/jarvis</string>
<string>serve</string>
<string>--host</string>
<string>127.0.0.1</string>
<string>--port</string>
<string>8000</string>
</array>
<!-- To expose on the LAN: set host to 0.0.0.0 and uncomment this block.
<key>EnvironmentVariables</key>
<dict>
<key>OPENJARVIS_API_KEY</key>
<string>REPLACE_WITH_A_REAL_KEY</string>
</dict>
-->
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/openjarvis.stdout.log</string>
<key>StandardErrorPath</key>
<string>/tmp/openjarvis.stderr.log</string>
</dict>
</plist>
Key-by-Key Explanation
| Key | Value | Description |
|---|---|---|
Label |
com.openjarvis |
Unique identifier for the service. Used with launchctl commands to manage the service. |
ProgramArguments |
["/usr/local/bin/jarvis", "serve", "--host", "127.0.0.1", "--port", "8000"] |
The command and arguments to execute. Binds loopback by default; see the note above to expose on the LAN with an API key. |
RunAtLoad |
true |
Start the service immediately when the plist is loaded (and on each login). |
KeepAlive |
true |
Automatically restart the service if it exits for any reason. launchd monitors the process and relaunches it. |
StandardOutPath |
/tmp/openjarvis.stdout.log |
File where standard output is written. Contains server startup messages and access logs. |
StandardErrorPath |
/tmp/openjarvis.stderr.log |
File where standard error is written. Contains error messages and stack traces. |
Viewing Logs
Server output is written to the two log files specified in the plist:
# View standard output (startup messages, access logs)
cat /tmp/openjarvis.stdout.log
# View standard error (errors, warnings)
cat /tmp/openjarvis.stderr.log
# Follow logs in real time
tail -f /tmp/openjarvis.stdout.log /tmp/openjarvis.stderr.log
!!! tip "Persistent log location"
Files in /tmp may be cleared on reboot. For persistent logs, change the paths in the plist to a permanent location:
```xml
<key>StandardOutPath</key>
<string>/Users/yourname/.openjarvis/openjarvis.stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/yourname/.openjarvis/openjarvis.stderr.log</string>
```
After changing the plist, unload and reload the service for the changes to take effect.
Managing the Service
Loading and Unloading
# Load the service (starts it due to RunAtLoad)
launchctl load ~/Library/LaunchAgents/com.openjarvis.plist
# Unload the service (stops it and prevents it from starting at login)
launchctl unload ~/Library/LaunchAgents/com.openjarvis.plist
Starting and Stopping
If the service is loaded but you want to manually stop or start it without unloading:
# Stop the service
launchctl stop com.openjarvis
# Start the service
launchctl start com.openjarvis
!!! warning
Because KeepAlive is set to true, using launchctl stop will cause launchd to restart the service almost immediately. To fully stop the service, use launchctl unload instead.
Checking Status
# List all loaded services matching "openjarvis"
launchctl list | grep openjarvis
The output columns are:
| Column | Description |
|---|---|
| PID | Process ID (or - if not running) |
| Status | Last exit status (0 = normal) |
| Label | The service label (com.openjarvis) |
Configuration Changes
Changing the Port or Host
Edit the ProgramArguments array in the plist. Each argument must be a separate <string> element:
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/jarvis</string>
<string>serve</string>
<string>--host</string>
<string>127.0.0.1</string>
<string>--port</string>
<string>9000</string>
</array>
Specifying an Engine and Model
Add additional arguments to the array:
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/jarvis</string>
<string>serve</string>
<string>--host</string>
<string>0.0.0.0</string>
<string>--port</string>
<string>8000</string>
<string>--engine</string>
<string>ollama</string>
<string>--model</string>
<string>qwen3:8b</string>
</array>
Setting Environment Variables
Add an EnvironmentVariables dictionary to the plist:
<key>EnvironmentVariables</key>
<dict>
<key>OPENJARVIS_ENGINE_DEFAULT</key>
<string>ollama</string>
<key>OPENJARVIS_OLLAMA_HOST</key>
<string>http://localhost:11434</string>
</dict>
Using a Different jarvis Binary Path
If jarvis is installed in a virtual environment or a non-standard location, update the first element of ProgramArguments:
<key>ProgramArguments</key>
<array>
<string>/Users/yourname/.local/bin/jarvis</string>
<string>serve</string>
<string>--host</string>
<string>0.0.0.0</string>
<string>--port</string>
<string>8000</string>
</array>
Applying Changes
After editing the plist file, unload and reload the service:
launchctl unload ~/Library/LaunchAgents/com.openjarvis.plist
launchctl load ~/Library/LaunchAgents/com.openjarvis.plist
System-Wide Installation
The instructions above install the service as a user agent (runs only when you are logged in). To run OpenJarvis as a system-wide daemon that starts at boot regardless of user login:
- Copy the plist to
/Library/LaunchDaemons/(requiressudo). - Set the file ownership to
root:wheel. - Optionally add a
UserNamekey to run as a specific user.
sudo cp deploy/launchd/com.openjarvis.plist /Library/LaunchDaemons/
sudo chown root:wheel /Library/LaunchDaemons/com.openjarvis.plist
sudo launchctl load /Library/LaunchDaemons/com.openjarvis.plist
!!! note
System daemons in /Library/LaunchDaemons/ run as root by default. Add a UserName key to run as a less-privileged user:
```xml
<key>UserName</key>
<string>openjarvis</string>
```