Talking to a sheep
An app that re-reads its configuration on SIGHUP can be reconfigured with shep signal <name> HUP without being restarted, so it keeps every warm cache, open pool and in-flight request it was holding. That is a pattern shep can reach and cannot offer on its own: restart and reload both replace the process. This page is the three verbs that don't.
Signals
shep signal <selector> <signal>Delivers one signal to each matched sheep's own process, never to its process group. The lambs it forked are not signalled. That is the difference between this verb and the stop ladder: shep stop signals the whole group and then escalates, and this one is a nudge to a single process with nothing behind it.
A config change is not a binary upgrade
shep prices those two identically, and your app almost certainly does not. Replacing a process throws away everything it had built up in memory, which is the right price for new code and a steep one for a changed setting. For a process holding state that is expensive to rebuild, the gap between the two is the whole reason this verb is worth knowing about:
shep restart apireplaces the process. The new one reads the new config, and also starts from nothing: caches cold, pools empty, whatever was in flight gone.shep signal api HUPleaves the process where it is. If the app re-reads its config onSIGHUP, it picks up the change and keeps everything else.
Only the application can make the second one true. shep delivers the signal and reports that it was delivered; what SIGHUP means is the app's convention, though a widely kept one. Server software has treated it as "re-read your configuration" for decades, and plenty of application frameworks and long-running daemons do the same. Here is a sheep that traps it, saying so in its own output:
$ shep signal web HUP ID NAME OUTCOME DETAIL 0 web delivered 1 web delivered $ shep bleats web --no-follow --lines 3 web:0 | reloaded config
The nine names
SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH, SIGCONT and SIGKILL. The SIG prefix and the case are both optional, so hup, Hup and SIGHUP are the same request, and the wire carries the canonical spelling whichever you typed. Anything outside the nine is refused locally, before the command opens a connection at all.
A raw number is refused with them, deliberately. Signal numbers are not portable: SIGUSR1 is 10 on Linux and 30 on macOS, so a command that took one would mean two different things on two hosts and shep will not guess which you meant.
SIGSTOP is the one real, spellable, deliverable signal that is missing on purpose. A stopped process still reads online in shep flock, in describe, on the bus and to every dog, because the shepherd owns no mechanism that could tell the difference. Refusing it keeps shep out of a flock state it cannot describe. SIGCONT is accepted, so an operator who stopped a sheep by some other route has a way back.
$ shep signal web SIGSTOP error[usage]: `SIGSTOP` is not a signal shep will send; accepted: SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH, SIGCONT, SIGKILL $ echo $? 2 $ shep signal nope HUP error[not_found]: the daemon reported NotFound: selector matched no registered sheep $ echo $? 3
delivered means the kernel took it, not that the app did anything with it. A signal the app blocks or ignores reads exactly like one it acts on, because there is nothing on this path that could tell them apart and a supervisor inventing a distinction would be guessing.
That cuts the other way too, and this is the one to be careful with: a signal the app does not handle still has a kernel default, and for most of the nine that default is to terminate the process. A sheep that never installed a SIGUSR1 handler is killed by one, the row still says delivered, and the exit shows up in theEXIT column like any other:
$ shep signal batch USR1 ID NAME OUTCOME DETAIL 3 batch delivered $ shep flock ID NAME STATUS PID RESTARTS EXIT CPU MEM UPTIME FOLD SMIT 3 batch stopped - 0 SIGUSR1 - - 0s - - 0 web:0 online 54244 0 - 0.1% 3.0M 33s - - 1 web:1 online 54338 1 - 0.1% 3.0M 26s - - 2 worker online 54639 2 - 0.1% 3.0M 10s - -
batch above sets autorestart = false, which is why it stayed down. With the default autorestart it would have come straight back with its restart counter one higher, and a shep signal all USR2 across a flock you have not checked is a restart of everything in it that does not handle SIGUSR2.
Which sheep the selector reaches
The same grammar every other verb takes: a name, a numeric id, name:slot for one instance of a clustered app, a glob, a /regex/, fold:<name>, or all. A bare name reaches every instance of that app, so shep signal web HUP against three instances is three deliveries and three rows.
The NAME column carries the app's name, not the web:0 spelling shep flock uses, so ID is what tells two instances apart in a reply. Dogs are reached only by a selector that named one exactly: shep signal all HUP passes every dog by, and shep signal metrics HUP does not.
$ shep signal web:1 usr1 ID NAME OUTCOME DETAIL 1 web delivered $ shep signal all WINCH ID NAME OUTCOME DETAIL 0 web delivered 1 web delivered 2 worker delivered $ shep signal worker HUP ID NAME OUTCOME DETAIL 2 worker not_running no live process to signal
A sheep being drained by a reload is signalled like any other. It is a live process the selector matched, and holding it back would be a silent refusal with nowhere to explain itself.
What the rows say, and what the exit code says
Three outcomes per matched sheep. delivered: the kernel accepted the signal for that pid. not_running: the sheep is registered but has no live process, so it is stopped, errored, or waiting out a restart backoff. failed: the kernel refused the delivery, and DETAIL carries its reason, such as ESRCH for a process reaped between the lookup and the syscall or EPERM for one this shepherd may not signal.
None of the three fails the command. A selector like fold:backend makes a mixed flock the normal case, so the per-sheep answer lives in the row and the exit code answers a different question: whether the request worked. Exit 2 for a malformed selector or a signal name outside the nine, both caught locally before anything reaches the shepherd. Exit 3 when the selector matched no registered sheep at all.
What it is not
shep signal <name> TERM is not shep stop. A stop runs the ladder: the configured kill_signal to the whole process group, then kill_timeout, then SIGKILL, and the sheep ends up marked stopped. Sending SIGTERM here does none of that. There is no ladder, no timeout, no escalation and no stopped marking, so whatever the app's restart policy says happens next is what happens.
shep signal <name> KILL is a real SIGKILL to one process, and the restart policy reads the exit as any other unexpected one: an app with autorestart on comes back. And swapping instances is shep reload, not a signal an app happens to interpret that way.
On Windows only SIGKILL is delivered, and the other eight names are refused one at a time rather than the whole verb failing. Seven of them have no delivery mechanism to a foreign Windows process at all. SIGINT is the judgement call: the console control event exists, but Ctrl+C is disabled by default in a process group created the way shep creates every sheep's, so it would arrive nowhere while reading as success.
A line on its stdin
shep whisper <selector> <line>Writes one line to each matched sheep's stdin. Only reaches an app whose Flockfile sets stdin = true, and nothing else implies it: unlike the shepherd channel, which wait_ready and shutdown_with_message both turn on, nothing inside shep needs a sheep's stdin except this verb. One line, and the terminator is shep's to add, so a line containing a newline or a carriage return is a usage error rather than two commands.
$ shep whisper echoer "reload" ID NAME OUTCOME DETAIL 4 echoer sent $ shep bleats echoer --no-follow --lines 5 echoer | got: reload $ shep whisper worker "hello" ID NAME OUTCOME DETAIL 2 worker no_stdin no stdin pipe — set stdin = true
sent means the bytes were written and flushed to the pipe, not that the app read them. A pipe holds 64 KiB before it blocks, so a short line to an app that never reads its stdin is still sent. no_stdin is one outcome for two causes, the field being unset and the sheep not running, because the row is read to answer "why did my line not arrive" and both answers are "there is no pipe here".
not_written carries which of three things happened: the far end went away, the line arrived to find that sheep's queue already full, or the write did not finish inside the shepherd's own bound.
That last one does not promise the line was never written. A write that timed out is a write the shepherd stopped waiting for: the bytes may be sitting part-written in a pipe the app is not draining, and they land in full the moment it drains. Lines still queued behind it are dropped once their caller gives up, so a retry cannot pile duplicates up and deliver them together later, but treat the retry as a second command rather than a repeat of the first.
A named action
The third way in is shep trigger, which sends an action name and optional parameters over the sheep's shepherd channel and reports what the app answered. It is the only one of the three where the app talks back, and it needs the channel open: channel = true, or either of the two fields that imply it. It has a page to itself.