Skip to main content
reticle stop reads the pid recorded for the port and stops that process. reticle stop --force asks who is listening on the port instead, and frees it.
reticle kill was removed. It prints that stop --force replaces it and exits 1.

Flags

How it stops

  1. Ask the daemon to stop with POST /shutdown on loopback, with the pairing token. The daemon then runs its normal shutdown path on every OS, Windows included. An older daemon without the route, or a refused request, gets SIGTERM instead. Then poll every 100ms.
  2. Still alive after 5 seconds: SIGKILL. A daemon that ignores SIGTERM is wedged, which is the one situation this command exists for, and giving up there left the port held and a human reaching for kill -9.
  3. Still alive 2 seconds after that: report reticle_daemon_stop_timeout with the pid and exit 1. Surviving SIGKILL means the pid is not yours to kill, or it is an unkillable zombie. Neither is retryable.

What it prints

Real capture, stopping a port with no live pid recorded for it. It exits 0, because “already not running” is the state you asked for:
With --quiet that same run prints nothing at all and still exits 0.

Exit codes

Do not stop a daemon with lsof -ti tcp:4400 | xargs kill -9. That pattern matches the agent’s own reticle mcp proxy as well as the daemon and takes both down. Use this command, or reticle stop --force.

Freeing the port: --force

stop starts from the pid Reticle recorded. When that pid is dead or missing but a Reticle daemon still answers /status on the port (a stale pid file, or a daemon from another checkout), plain stop frees that listener the same way --force does. When nothing answers /status, plain stop reports reticle_daemon_not_running. stop --force always asks the port first, and is the command to reach for when the pid file and the port disagree.
Never free the port with lsof -ti tcp:4400 | xargs kill -9. reticle mcp holds a client socket on that port, so -ti lists the proxy right beside the daemon and the pipeline kills both. When the proxy dies, the agent’s next tool calls get no reply at all: not an error, not a timeout. Nothing is written to the proxy log either, because the process that writes it is the one that died. That is the mechanism behind most “my MCP disconnected” reports.

How --force decides

  1. Look up the listener with lsof -nP -iTCP:<port> -sTCP:LISTEN. Client sockets are excluded by construction, so the proxy is never a candidate.
  2. It is Reticle’s if it answers /status, or if it is the pid Reticle recorded for the port. /status outranks the pid file, so a daemon started from another checkout is still recognised as ours.
  3. Anything else is refused, by name: it is someone else’s process.
  4. For a Reticle daemon, POST /shutdown first, as above; else SIGTERM. Then SIGKILL after 5 seconds, then report that it survived and exit 1.

What --force prints

Real captures, taken against a daemon started on a scratch port. The long note and reason strings are trimmed at the ellipsis. Measured exit codes: 0 for killed and nothing-to-do, 1 for refused. Killed:
Refused, because the listener is not ours. This one was captured with python3 -m http.server 4498 holding the port:
Nothing to free:
listenerIdentified: false means lsof could not run and the pid came from Reticle’s records rather than from an observed listener. Those are different claims, so the field says which one you got. With --force, exit 0 means the port is free and 1 means it is still held: refused, or the process survived SIGKILL.

Worked example

Last modified on November 4, 2026