service management
kasmos ships service unit files in contrib/ for both linux (systemd) and macos (launchd). they keep the orchestration daemon and the remote task store running persistently across reboots without requiring a dedicated shell session.
service units
| unit | binary | purpose |
|---|---|---|
kasmos | kas daemon start --foreground | orchestration daemon — claims signals and advances plan lifecycle |
kasmosdb | kas serve | remote task store http server on port 7433 |
linux (systemd)
both units run under your user account (systemctl --user) and require an absolute path to the kas binary. the contrib/*.service files are templates — the __KAS_BIN__ placeholder must be substituted before enabling a unit.
preferred workflow (just)
# install and start both services
just services-enable
# orchestration daemon only
just kasmosd-enable
# task store server only
just db-service-enable
each just recipe resolves the kas binary via command -v kas, renders the .service template with the absolute path, writes the result to ~/.config/systemd/user/, runs systemctl --user daemon-reload, and enables + starts the unit.
manual installation
if you do not have just installed:
mkdir -p ~/.config/systemd/user
# orchestration daemon
sed "s|__KAS_BIN__|$(command -v kas)|g" \
contrib/kasmos.service \
> ~/.config/systemd/user/kasmos.service
systemctl --user daemon-reload
systemctl --user enable --now kasmos
# task store
sed "s|__KAS_BIN__|$(command -v kas)|g" \
contrib/kasmosdb.service \
> ~/.config/systemd/user/kasmosdb.service
systemctl --user daemon-reload
systemctl --user enable --now kasmosdb
checking service status
systemctl --user status kasmos
systemctl --user status kasmosdb
reading logs
journalctl --user -u kasmos # orchestration daemon logs
journalctl --user -u kasmosdb # task store logs
journalctl --user -u kasmos -f # follow live output
journalctl --user -u kasmos --since "1 hour ago"
stopping and disabling
systemctl --user stop kasmos
systemctl --user disable kasmos
systemctl --user stop kasmosdb
systemctl --user disable kasmosdb
service file contents
contrib/kasmos.service (template — __KAS_BIN__ is substituted by just kasmosd-install):
[Unit]
Description=kasmos orchestration daemon
After=network.target
[Service]
Type=simple
ExecStart=__KAS_BIN__ daemon start --foreground
ExecStop=__KAS_BIN__ daemon stop
Restart=on-failure
RestartSec=5
Environment=HOME=%h
StandardOutput=journal
StandardError=journal
SyslogIdentifier=kasmos
[Install]
WantedBy=default.target
installed unit example (with kas at /home/you/go/bin/kas):
ExecStart=/home/you/go/bin/kas daemon start --foreground
ExecStop=/home/you/go/bin/kas daemon stop
contrib/kasmosdb.service (template — __KAS_BIN__ is substituted by just db-service-install):
[Unit]
Description=kasmos plan store server
After=network.target
[Service]
Type=simple
ExecStart=__KAS_BIN__ serve
Restart=on-failure
RestartSec=5
Environment=HOME=%h
[Install]
WantedBy=default.target
both units use Restart=on-failure so they recover from crashes without manual intervention.
macos (launchd)
on macos, kasmos uses launchd plists in contrib/. plists are loaded per-user via launchctl and live in ~/Library/LaunchAgents/.
preferred workflow (just)
# install and load both agents
just services-enable
# orchestration daemon only
just kasmosd-enable
# task store server only
just db-service-enable
each just recipe renders the plist template with your $HOME and the path to the kas binary, copies it to ~/Library/LaunchAgents/, and calls launchctl load -w to load it immediately.
manual installation
mkdir -p ~/Library/LaunchAgents ~/Library/Logs/kasmos
# orchestration daemon
sed "s|__KAS_BIN__|$(command -v kas)|g; s|__HOME__|$HOME|g" \
contrib/com.kasmos.daemon.plist \
> ~/Library/LaunchAgents/com.kasmos.daemon.plist
launchctl load -w ~/Library/LaunchAgents/com.kasmos.daemon.plist
# task store
sed "s|__KAS_BIN__|$(command -v kas)|g; s|__HOME__|$HOME|g" \
contrib/com.kasmos.taskstore.plist \
> ~/Library/LaunchAgents/com.kasmos.taskstore.plist
launchctl load -w ~/Library/LaunchAgents/com.kasmos.taskstore.plist
checking service status
launchctl list | grep kasmos
look for com.kasmos.daemon and com.kasmos.taskstore. a pid in the first column means the service is running.
reading logs
tail -f ~/Library/Logs/kasmos/daemon.log # orchestration daemon logs
tail -f ~/Library/Logs/kasmos/taskstore.log # task store logs
stopping and unloading
# stop and unload
launchctl unload -w ~/Library/LaunchAgents/com.kasmos.daemon.plist
launchctl unload -w ~/Library/LaunchAgents/com.kasmos.taskstore.plist
# to also delete the plist files (permanent removal)
rm ~/Library/LaunchAgents/com.kasmos.daemon.plist
rm ~/Library/LaunchAgents/com.kasmos.taskstore.plist
plist template contents
contrib/com.kasmos.daemon.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.kasmos.daemon</string>
<key>ProgramArguments</key>
<array>
<string>__KAS_BIN__</string>
<string>daemon</string>
<string>start</string>
<string>--foreground</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>__HOME__</string>
</dict>
<key>StandardOutPath</key>
<string>__HOME__/Library/Logs/kasmos/daemon.log</string>
<key>StandardErrorPath</key>
<string>__HOME__/Library/Logs/kasmos/daemon.err.log</string>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
contrib/com.kasmos.taskstore.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.kasmos.taskstore</string>
<key>ProgramArguments</key>
<array>
<string>__KAS_BIN__</string>
<string>serve</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>__HOME__</string>
</dict>
<key>StandardOutPath</key>
<string>__HOME__/Library/Logs/kasmos/taskstore.log</string>
<key>StandardErrorPath</key>
<string>__HOME__/Library/Logs/kasmos/taskstore.err.log</string>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
launchd's KeepAlive=true setting is the equivalent of Restart=on-failure — launchd will restart the process if it exits.
running the daemon directly (without a service manager)
for debugging or one-shot runs, start the daemon in the foreground:
kas daemon start --foreground
or use just:
just kasmosd-start
check running daemon status:
kas daemon status
stop the daemon:
kas daemon stop
# or:
just kasmosd-stop
diagnosing installation
use the built-in doctor command to verify the daemon and task store are installed and responding:
just doctord
output example (linux):
== kasmos daemon doctor ==
binary:
/home/user/go/bin/kas
user units:
- kasmos.service: installed, enabled=enabled, active=active
- kasmosdb.service: installed, enabled=enabled, active=active
daemon api:
daemon socket present: /run/user/1000/kasmos/kas.sock
next steps:
- just kasmosd-enable # install + enable orchestration daemon
- just db-service-enable # install + enable plan store service
- just services-enable # enable both services
- just kasmosd-start # direct foreground/background daemon start
why the daemon matters for signals
when an agent emits mcp signal_create (or, as a fallback, kas signal emit ...), the signal is written as a pending row in the task store database. the orchestration daemon is what actually claims that row and advances the plan's lifecycle state (e.g. from implementing → reviewing).
if you emit signals without a running daemon, signals accumulate as pending rows but plans do not advance. start the daemon — either via the service manager or kas daemon start --foreground — before running unattended agent workflows that rely on signal-driven orchestration.
related
- remote task store —
kas serveconfiguration and rest api - daemon — orchestration daemon internals
- cli reference —
kas daemonandkas serveflags