Daemon & configuration

loopkeepd is the resident process that does everything: watches triggers, executes runs, evaluates policies, writes the event log, delivers notifications. The CLI and the desktop app are clients — closing them changes nothing about what's running.

One daemon serves every registered workspace. Each workspace keeps its own independent context: its own event log, policy, trust state, and workflows.

Control

lk start      # alias: up
lk stop       # alias: down — in-flight runs are restored as interrupted on the next start
lk restart
lk status

Clients talk to the daemon over a local socket, /tmp/loopkeepd.sock by default (LOOPKEEP_SOCKET overrides — set it for both the daemon and the clients). No network port is opened; the Console connection, if you pair, is a single outbound WebSocket.

Files on disk

Global, under ~/.config/loopkeep/ (respects XDG_CONFIG_HOME; LOOPKEEP_CONFIG overrides the config file path):

~/.config/loopkeep/
├── config.yaml       # daemon settings (concurrency cap, retention)
└── policy.yaml       # your home policy layer

Per workspace, under <workspace>/.loopkeep/:

.loopkeep/
├── policy.yaml           # project policy (commit this)
├── policy.local.yaml     # your personal overrides (gitignore this)
├── workflows/            # workflow definitions
├── secrets               # secret NAMES only — values live in the OS keychain
├── events.db             # append-only event log (SQLite)
└── worktrees/<run_id>/   # each run's isolated git worktree

config.yaml

concurrency:
  global_max: 4 # runs in flight across every workspace
retention:
  worktree_days: 14 # how long finished runs' worktrees are kept; omit to keep forever

Both are managed from the CLI (lk global-max, lk retention) and the desktop app; edits are persisted back to this file either way.

The event log

Everything the system does — trigger firings, state changes, proposed actions, your decisions — is an immutable record in events.db, per workspace. The inbox, the runs list, and the audit trail are all derived views of this log; there is no second bookkeeping to drift out of sync. Records are never updated or deleted — corrections are new events. Treat the file as read-only evidence; if it can't be persisted at startup, the daemon refuses to start rather than silently losing history.

This is also where supervision evidence comes from: who approved what, when, with which rule in effect, is a query — not a reconstruction.

Workspace registry

lk workspaces                          # list
lk workspaces add <path> [--name <n>]
lk workspaces remove <id>              # home cannot be removed

Commands addressed at an unregistered path that contains .loopkeep/ register it on the fly.

Troubleshooting

SymptomLikely cause
CLI reports the daemon isn't runninglk start; if you use a custom LOOPKEEP_SOCKET, make sure daemon and CLI agree on it.
A workflow never fires automaticallyWorkspace untrusted (lk trust), the workflow disabled (lk enable <workflow>), or the global pause is on (lk resume --all).
A run sits in waitingIt's waiting for you — check lk inbox, decide with lk approve <seq>.
A run shows interrupted after a restartExpected: lk stop restores in-flight runs that way on the next start. lk rerun it.
run_* worktrees pile upRetention is off or long — lk retention <days>, or sweep now with lk cleanup.