Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Systemd (non-NixOS)

For hosts without the NixOS module, the unit below is a drop-in that mirrors what the module generates with services.omw.hardening = true (in the repo: assets/omw.service):

[Unit]
Description=OMW agent runtime
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=omw
Group=omw
WorkingDirectory=/var/lib/omw
EnvironmentFile=/etc/omw/env
ExecStart=/usr/local/bin/omw loop --config /etc/omw/omw.toml
Restart=on-failure
RestartSec=5s
StateDirectory=omw

# Systemd sandboxing. Mirrors `services.omw.hardening = true` from the NixOS
# module. `MemoryDenyWriteExecute` is deliberately absent: it would break the
# wasmtime JIT and nodejs MCP servers. `PrivateDevices=false` keeps /dev
# usable for MCP servers and bwrap wrappers. `LimitMEMLOCK=infinity` is
# required: secrets are mlock()ed and the empty `CapabilityBoundingSet`
# drops CAP_IPC_LOCK, so a zero memlock limit would fail every secret with
# EPERM. Add `BindReadOnlyPaths=` entries if brains or the config live outside
# /var/lib/omw, and `ReadWritePaths=` entries for filesystem MCP workspaces
# outside the state directory.
NoNewPrivileges=true
PrivateDevices=false
PrivateIPC=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectKernelModules=true
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=true
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
SystemCallArchitectures=native
UMask=0077
LimitMEMLOCK=infinity
CapabilityBoundingSet=
AmbientCapabilities=

[Install]
WantedBy=multi-user.target

Install it (the assets/… paths are repo-relative):

sudo useradd -r -d /var/lib/omw omw
sudo install -m 644 assets/omw.service /etc/systemd/system/omw.service
sudo mkdir -p /etc/omw
sudo install -m 600 assets/omw.example.toml /etc/omw/omw.toml
sudo install -m 600 assets/omw.example.env /etc/omw/env
sudo systemctl daemon-reload
sudo systemctl enable --now omw

Secrets live in /etc/omw/env as OMW__-prefixed variables (see the deployment overview); they layer over /etc/omw/omw.toml at runtime. Put brains and the filesystem tooling workspace under /var/lib/omw (the unit’s StateDirectory + WorkingDirectory).

Hardening

The unit carries the same sandbox as the NixOS module (see the NixOS module for the rationale):

  • NoNewPrivileges, RestrictSUIDSGID, RestrictRealtime, LockPersonality, empty capability sets plus LimitMEMLOCK=infinity (for allowing secret protection against swapping) — safe, and compatible with bwrap-wrapped MCP servers.
  • ProtectSystem=strict + ProtectHome + ProtectProc=invisible + PrivateIPC — the filesystem is read-only outside API mounts and the state directory; allow-list extras with BindReadOnlyPaths= (brains or config outside /var/lib/omw) and ReadWritePaths= (filesystem MCP workspaces elsewhere).
  • RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 — provider egress plus local stdio MCP sockets.
  • MemoryDenyWriteExecute is deliberately absent: it would break the wasmtime JIT and nodejs MCP servers. PrivateDevices=false keeps /dev usable for MCP servers and bwrap wrappers.