docs · pre-release
Menu
Day to day / The lookout

The lookout

shep lookout (alias dash) is a live terminal dashboard over the shepherd. It reads the flock two ways at once: a subscription to the event bus so the screen moves as things happen, and a re-list every two seconds so a dropped event can't leave it quietly wrong.

careful
Lookout needs a real terminal. With stdout redirected (piped to a file or another program) it refuses outright rather than writing escape sequences somewhere nobody will read them.
note
If all you want is the flock table kept current, shep flock --follow redraws it in place with no panes and no action gate. Covered under terminal output.

Start it

$ shep lookout
$ shep lookout --read-only

--read-only closes the action gate, covered below. The gate is open by default.

The flock table

Every frame below is real: rendered headlessly through ratatui's TestBackend by a pinned snapshot test, not mocked up for this page. A > gutter to the left of ID marks the selected sheep. j/k move the selection one row at a time; g/G jump to the first and last row. Everything below the table describes whichever sheep is selected.

120×30
shep lookout   /home/ada/.shep                                                                            6 in the flock
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     0 errored · 0 parked   floc…
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ID    NAME        STATUS           PID      RESTARTS  EXIT       CPU     MEM       UPTIME    FOLD        SMIT
   ██ FLOCK
> 2     api         online           48219    1         -          7.1%    241.0M    1h 28m    edge        -
  3     billing-r…  online           48230    0         -          0.8%    96.0M     1h 29m    -           -
  4     cron        online           48233    0         -          0.1%    8.0M      1h 31m    -           -
  5     metrics     online           48240    0         -          0.4%    11.0M     1h 32m    -           -
  0     web         online           48211    0         -          3.4%    182.0M    1h 25m    edge        -
  1     web         online           48212    0         -          2.9%    178.0M    1h 26m    edge        -
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ SHEEP 2  api   online   pid 48219   restarts 1   uptime 1h 28m   cpu 7.1%   mem 241.0M   fold edge
lambs  not read yet
out  /home/ada/.shep/logs/api-2-out.log   │   err  /home/ada/.shep/logs/api-2-err.log   2.0K on disk

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ BLEATS api  … 1 earlier line not shown
out  GET /healthz 200 3ms
out  GET /v1/orders 200 44ms
out  POST /v1/orders 201 88ms
out  GET /v1/orders/8821 200 9ms
out  connection pool: 14/50 in use
q quit   j/k select   / filter   F folds   x stop   R restart   L reload   s settings   e edit   * your… control enabled

Rows sit under two reverse-video bands, FLOCK for the sheep and DOGS below it for anything shep is supervising as a dog. Each band shows up only when its side has a row.

Multi-instance apps

An app with more than one instance gets a group row above its slots, the same rollup shep flock shows: web ×3 in NAME, STATUS as the shared word or a count per state when the instances disagree, RESTARTS/CPU/MEM summed and UPTIME the minimum across them. The group row is selectable with j/k like any other row, and the cursor survives the two-second poll on either kind of row.

An action key on a selected group row is the one place in lookout a single keypress reaches more than one process, so the confirm states how many it's about to touch:

stop all 3 instances of web? enter confirms, any other key cancels

Enter sends the action against the whole app by name, not against one pinned id, and the detail pane on a group row shows the app-level summary rather than a single process's fields. The bleats feed can't follow a group, since there's no single log to re-read for it: it names the app and asks you to select one instance instead.

Grouping by fold

F gathers the table by fold, AppConfig::fold, instead of by name. Each fold gets a header row; sheep with no fold sit under a no fold band, and dogs, which are never in a fold, keep their own Dogs band. F again returns to the flat table.

A triangle marks each fold header: ▾ when its members are showing, ▸ when z has collapsed it. Member rows carry none, so the two kinds of row stay apart with every colour stripped.

160×30, fold view
shep lookout   /home/ada/.shep                                                                                                                   10 in the flock

host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     0 errored · 0 parked   flock cpu 20.6%       ▁█   flock mem 928.0M …
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  NAME        STATUS        SHARE                   MEM         CPU        UPTIME      RESTARTS  NOTES
  ▸ batch ×2  online        █░░░░░░░░░░░░░░░░░░░    32.0M       1.6%       1h 55m      3         3% of flock memory
  ▾ core ×1   online        ██░░░░░░░░░░░░░░░░░░    96.0M       2.0%       1h 58m      0         10% of flock memory
  worker      online                                96.0M       2.0%       1h 58m      0
> ▾ edge ×4   online        █████████████████░░░    781.0M      16.5%      15m         4         84% of flock memory
  api         online                                241.0M      7.1%       2h 4m       1
  web ×3      online                                540.0M      9.4%       15m         3
   ██ NO FOLD
  cron        online                                8.0M        0.1%       2h 6m       0
  metrics     online                                11.0M       0.4%       2h 7m       0
   ██ DOGS
  bark        online                                -           -          10m         0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
fold edge ×4  online   restarts 4   uptime 15m   cpu 16.5%   mem 781.0M
lambs  a fold has no single process to walk; select one sheep


────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ BLEATS edge  follows one sheep; select one inside this fold to see its log





q quit   j/k select   / filter   F flat   z collapse   x stop   R restart   L reload   s settings   e edit   * yours   ! parked                  control enabled

A fold header's restarts, CPU and memory are its members summed. Uptime is the exception: it's the shortest of the members', so the header reads as time since the fold was last disturbed rather than the age of its longest-lived sheep. The SHARE gauge and the NOTES percentage both show that fold's share of the whole flock's memory. A multi-instance app inside a fold still gets its own group row, the same web ×3 rollup it would draw in the flat table, so a fold never nests three levels deep.

z collapses the fold under the cursor, hiding its members and leaving the header's own rollup on screen. Pressed again it opens back up. It does nothing on the no fold orDogs bands, or anywhere outside the fold view.

An action key on a fold header reaches every sheep in it, the same way a group row's does, and the confirm names the count:

restart all 4 sheep in fold edge? enter confirms, any other key cancels

The no fold header isn't selectable. There's no selector that names "everything with no fold" on the wire, so an action there would have nothing to send.

The three panes underneath

The host strip is the line above the table: load average and host memory each draw a ten-cell gauge, load against the core count and memory against the host's total, with the gauge coming before its numbers. It also carries how many sheep are errored or parked, the flock's own summed CPU and memory, and how long the machine's been up. At 120 columns the frame above only has room for the first three; flock CPU, flock memory and uptime crowd off the edge before you can read them. Below the table, the detail pane restates the selected row in full sentences and adds what the row can't fit: the untruncated name and both log paths.

The detail pane also shows the selected sheep's lambs: a count of the descendants Describe's process-table walk found, each one's pid and executable name (and its label in brackets, when the sheep named it), and how long ago that reading was taken. It's fetched once when the selection actually moves onto a running sheep, not on the table's own two-second listing, so the age stamp is real: hold the cursor on one sheep and the lamb list gets stale rather than silently refreshing behind a timestamp that still says "just now". A stopped sheep has no pid to walk from, and the pane says that plainly rather than reporting zero lambs.

120×30, with lambs
shep lookout   /home/ada/.shep                                                                            6 in the flock
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     0 errored · 0 parked   floc…
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ID    NAME        STATUS           PID      RESTARTS  EXIT       CPU     MEM       UPTIME    FOLD        SMIT
   ██ FLOCK
> 2     api         online           48219    1         -          7.1%    241.0M    1h 28m    edge        -
  3     billing-r…  online           48230    0         -          0.8%    96.0M     1h 29m    -           -
  4     cron        online           48233    0         -          0.1%    8.0M      1h 31m    -           -
  5     metrics     online           48240    0         -          0.4%    11.0M     1h 32m    -           -
  0     web         online           48211    0         -          3.4%    182.0M    1h 25m    edge        -
  1     web         online           48212    0         -          2.9%    178.0M    1h 26m    edge        -
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ SHEEP 2  api   online   pid 48219   restarts 1   uptime 1h 28m   cpu 7.1%   mem 241.0M   fold edge
lambs  3 parent-pid descendants, read 9m 53s ago   48220 node   48221 node   48222 node
out  /home/ada/.shep/logs/api-2-out.log   │   err  /home/ada/.shep/logs/api-2-err.log

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ BLEATS api  … 1 earlier line not shown
out  GET /healthz 200 3ms
out  GET /v1/orders 200 44ms
out  POST /v1/orders 201 88ms
out  GET /v1/orders/8821 200 9ms
out  connection pool: 14/50 in use
q quit   j/k select   / filter   F folds   x stop   R restart   L reload   s settings   e edit   * your… control enabled

The bleats feed is the one pane that isn't built from the flock listing: it re-reads the selected sheep's out/err log files from disk on every refresh, rather than subscribing to the bus. That's a deliberate cost: a busy flock pays one bounded 64 KiB read per file per refresh, instead of making the dashboard the highest-volume subscriber on the bus for a pane most refreshes don't even redraw. When it can't show everything, it says so instead of guessing. Lines it read and dropped are counted exactly, and bytes below its window are reported in bytes, because nothing counted the lines inside them:

 ██ BLEATS api  … 525 earlier lines not shown, and 3.8M before them never read
 ██ BLEATS api  out then err  from the log files, re-read with each listing
this sheep has not written a log in this $SHEP_HOME

The sheep pane

↵ on a selected sheep opens a full-screen pane for it. Nothing happens on a group row, a fold header, or a dog: there's no single process to chart or config to show. esc closes it back to the table.

Two charts share a time axis: CPU history above, memory RSS below, one column per two-second poll. The window is whatever the terminal's width buys, stated in the chart's own header rather than promised as a constant: a fresh pane reads collecting until it has enough polls to fill its own body. The memory chart draws a ceiling line at max_memory when the sheep has one; with none set it scales to the window's own peak instead and says so.

160×48, the design size
shep lookout   /home/ada/.shep                                                                                                                    1 in the flock
 ██ SHEEP 20  web   online   pid 48500   restarts 0   uptime 1h 55m   cpu 20.0%   mem 30.0M   fold edge
██ CPU   collecting · 0m08s of 4m40s
    50%
      |
    38%                                                                                                                                           ▄
      |                                                                                                                                           █
    25%                                                                                                                                         █ █
      |                                                                                                                                         ████
    12%                                                                                                                                         ████
      0                                                                                                                                         ████
██ MEM   rss   same window   ·   ceiling 64.0M
 100.0M
  80.0M
  64.0M ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ceiling
  40.0M                                                                                                                                          ▄▄▄
      0                                                                                                                                        ▄████
        ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────now
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
██ CONFIG & ENV   e edit                                                    │ ██ BLEATS out then err · / narrow
                                                                            │out  listening on 0.0.0.0:8080
────────────────────────────────────────────────────────────────────────────│out  GET /healthz 200 3ms
process                                                                     │out  GET /v1/orders 200 44ms
cwd                         (unset)                                         │out  POST /v1/orders 201 88ms
depends_on                  (default)                                       │out  GET /v1/orders/8821 200 9ms
fold                        (unset)                                         │out  connection pool: 14/50 in use
group                       (unset)                                         │
instances                   3                                               │
interpreter                 (unset)                                         │
name                        web                                             │
reuse_port                  (default)                                       │
script                      ./srv                                           │
user                        (unset)                                         │
                                                                            │
────────────────────────────────────────────────────────────────────────────│
logging                                                                     │
err_file                    (unset)                                         │
merge_logs                  (default)                                       │
out_file                    (unset)                                         │
                                                                            │
────────────────────────────────────────────────────────────────────────────│
inputs                                                                      │
args                        ["--port","8080"]                               │
channel                     (default)                                       │
environment                 (unset)                                         │
stdin                       (default)                                       │

esc flock   e edit   J/K next sheep   x stop   R restart   L reload   b full log   / filter                                                      control enabled

Below the charts, a read-only listing of the sheep's config and env keys sits beside a BLEATS feed scoped to this sheep. e opens the same full editor the dashboard's own e does; b hands the embedded feed to the full-screen bleats pane, filter and all, rather than opening a fresh one; / filters it in place. J/K step to the next or previous sheep in the current sort and filter, re-fetching its config and re-scaling both charts without leaving the pane. x, R and L arm and confirm against the pinned sheep, the same gate and the same prompt the dashboard's own action keys use.

Narrower terminals give up the second chart before the first: below 140 columns the memory chart folds into one line, an RSS reading and a ten-cell gauge; below 100 both charts fall back to the flock table's own CPU 20s sparkline and MEM/CEIL gauge, one row. The config and feed columns are what the pane is for, so they give up ground last: the memory chart drops below 26 rows, the CPU chart below 21.

99×48, the sparkline fallback
shep lookout   /home/ada/.shep                                                       1 in the flock
 ██ SHEEP 20  web   online   pid 48500   restarts 0   uptime 1h 55m   cpu 20.0%   mem 30.0M   fold…
CPU 20s         ▆▅█▅      MEM/CEIL   █████░░░░░
esc flock   e edit   J/K next sheep   x stop   R restart   L reload   b full log  … control enabled

The bleats pane, full screen

b opens the selected sheep's feed full screen and pins it there. There's no table on screen anymore to change the selection with, so the pane keeps describing that one sheep until it closes. It reads the same log files the corner feed does, just polled faster while it has focus.

Three filters stack on top of the feed, and a line has to pass all three to show. o cycles the stream (out, err, or both), m cycles a minimum level, and / opens a box for a text or regex match, with matches highlighted. A level is a bare word like WARN, or a level=warn pair, so the filter works on logfmt output too. An app that writes neither can declare its own patterns, with level_rules in its Flockfile. A line with no detectable level always shows, whatever the minimum is: most app output carries no level word at all, and hiding it would defeat the reason the pane exists. The filter row names which axes are set and counts how many lines in the current window survive them.

100×14, filtered and wrapped
shep lookout   /home/ada/.shep                                                        6 in the flock
 ██ api  out /home/ada/.shep/logs/api-2-out.log  err /home/ada/.shep/logs/api-2-err.log  64.0K wind…
 stream out   level ≥ warn   match /retry|jitter/ (regex)  1 of 16 lines in the window: all three m…
out  WARN retrying upstream payment gateway after a timeout, backing off 500ms before trying again w
     ith jitter added so the whole fleet does not retry at once
esc back   j/k line   ctrl-d/u page   G end   / search   n/N match   f follow   w wrap … █ following

esc drops the newest filter first, one at a time, and only closes the pane once none are left. j/k scroll a line, ctrl-d/ctrl-u a page, G jumps to the end and starts following again, f toggles following, w wraps long lines instead of truncating them, and n/N step between matches.

Filtering the table

/ opens a query box in the status bar; typing narrows the table to sheep whose name contains it, case-insensitively, as you type. Enter applies the query and closes the box, Esc clears it back to the whole flock, and both leave the cursor on a real row: if the sheep it was on drops out of the narrowed set, the selection moves to whatever now sits in the same position rather than pointing at a row the table has stopped drawing. The title always carries the flock's true size beside the filtered count, so a narrow query can't be read as a small flock, and the host strip above the table keeps summing the whole flock too, not just the two rows the filter leaves on screen, because it describes the machine, not the table's current view. At 100 columns the strip's gauges crowd those totals off the edge before you can read them, but the filter still hasn't touched what they'd be summing.

100×14, filter applied
shep lookout   /home/ada/.shep                                                   2 of 6 in the flock
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     0 error…
────────────────────────────────────────────────────────────────────────────────────────────────────
  ID    NAME               STATUS           PID      RESTARTS  EXIT       CPU     MEM       UPTIME
   ██ FLOCK
> 0     web                online           48211    0         -          3.4%    182.0M    1h 25m
  1     web                online           48212    0         -          2.9%    178.0M    1h 26m
filter "web"   / edit   esc clear                                                    control enabled

The keymap

h or ? opens a box over whatever is on screen, listing every key lookout binds. Four columns group them by what the key does: MOVING for the selection and the panes, LOOKING for what's on screen, CHANGING for anything that writes, and DOING for the three that act on a sheep.

It owns the keyboard while it is up. h, ? and Esc close it and q still quits. A close dialog is the one screen it will not open over.

The line under the three destructive keys says whether they can act at all. With the gate open it reads control enabled, with --read-only it reads read-only, and once the link is down it says so instead, because x, R and L then refuse for a reason the gate doesn't name. The overlay itself still opens on a frozen dashboard, which is where a key list is wanted most.

It drops columns as the terminal narrows, and under 13 rows it refuses and names the height it needs, since half a key list is worse than a sentence saying it does not fit.

When the terminal is small

Panes drop before columns do, least-diagnostic-first: the detail pane goes, then the feed, then the host strip, leaving the flock table. Within the table, columns drop one at a time in a fixed order, ending at ID NAME STATUS. MEM/CEIL and CPU 20s go first, because a gauge and a sparkline restate numbers that CPU and MEM already carry.

Every row's sparkline scales to the same ceiling, the busiest sample any sheep has posted in the retained window, so rows read against each other rather than each one filling its own column.

51×14
shep lookout   /home/ada/.shep       6 in the flock
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores  …
───────────────────────────────────────────────────
  ID    NAME      STATUS           CPU     UPTIME
   ██ FLOCK
> 2     api       online           7.1%    1h 28m
  3     billing…  online           0.8%    1h 29m
  4     cron      online           0.1%    1h 31m
  5     metrics   online           0.4%    1h 32m
  0     web       online           3.4%    1h 25m
  1     web       online           2.9%    1h 26m
q quit   j/k select   / filter   F… control enabled

The settings screen has its own drop order, and each of its two tables keeps whichever fact is not said anywhere else on the screen.

45×24
shep lookout   /home/ada/.shep 2 in the flock
  [daemon]
> log_level        warn           shep.toml
  log_json         false          the default
  socket           /home/ada/.s…  shep.toml
  max_cron_sleep   30s            shep.toml

  [whistle]
  allow_control    false          the default

  [style]
  level            full           shep.toml

  [dogs]
  space arms, Enter applies; a dog needs no …
  NAME               IN FILE  RUNNING
  otel               no       online
  ledger             yes      not running
  bark               yes      silent
esc/s close   j/k select   g… control enabled

Below 33 columns or 6 rows, lookout refuses to draw rather than risk overlapping garbage. The refusal message is short enough to survive the terminal it's warning about:

28×8
too small
need 33x6

The secrets pane has a floor of its own, at 74 columns. Its narrowest tier is KEY, VALUE and IN FORCE, and a key with no value and no scope answers nothing, so there is nothing narrower to fall back to. Below 74 the pane says so, and the rest of lookout still draws at 33.

If the shepherd goes away

Lookout re-dials five times (at 250/500/1000/2000/4000 ms, about 7.75 seconds total) before it gives up. Then it stops polling and leaves the last known values on screen, with the band saying so and the whole table in one muted grey: nothing there is current, so nothing there is allowed to look current. UPTIME becomesFROZEN, because a clock still ticking after everything around it stopped would be lying about a specific sheep by name.

The sheep detail band and the bleats feed hand their rows to a link panel. Both describe a shepherd that is gone; the panel describes what happened to it, quoting the last dial's own error. Lookout never exits on its own. q quits, h opens the keymap, and j/k and g/Gstill move over values that are already history. What no key can do is reach the shepherd, which is what the bar says instead of listing refusals: r is refused outright, since the task that would answer a re-dial has already returned by the time a freeze lands, so it says the shepherd is gone rather than pretending to try.

160×30, shepherd dead
 THE SHEPHERD HAS DIED  ▖  these values are frozen as of 2026-08-14 14:32:07                                                 nothing here is live  ▖  q to quit

 shep lookout   /home/ada/.shep  ·  the dashboard stays up so you can read what it had  ·  it will not exit on its own
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     1 errored · 0 parked   flock cpu 6.7%       ▁█   flock mem 371.0M  …
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

  ID    NAME                  STATUS           PID      RESTARTS  EXIT       CFG   CPU 20s     CPU     MEM/CEIL    MEM       FROZEN    FOLD        SMIT
   ██ FLOCK
> 2     api                   errored          -        14        1          -             ▁▁  -       ░░░░░░░░░░  -         1h 18m    edge        -
  3     billing-reconciliat…  waiting-restart  -        3         1          -             ▁▁  -       ░░░░░░░░░░  -         1h 19m    -           -
  4     cron                  stopped          -        0         SIGTERM    -             ▁▁  -       ░░░░░░░░░░  -         1h 21m    -           -
  5     metrics               online           48240    0         -          -              ▂  0.4%    ░░░░░░░░░░  11.0M     1h 22m    -           -
  0     web                   online           48211    0         -          -              █  3.4%    ░░░░░░░░░░  182.0M    1h 15m    edge        -
  1     web                   online           48212    0         -          -              ▇  2.9%    ░░░░░░░░░░  178.0M    1h 16m    edge        -









────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ THE LINK  5 redials over 7.75s, all refused   ·   lookout has stopped polling and stopped re-dialling
  ladder    █ 250ms  █ 500ms  █ 1s  █ 2s  █ 4s
  refused   the shepherd did not answer: could not connect to `/home/ada/.shep/run/shep.sock`: Connection refused (os error 61)
  since     2026-08-14 14:32:07, 9m 53s ago      the sheep themselves may well still be running; lookout cannot see them to say
  try       shep muster, from another shell      then reopen lookout: it does not reconnect on its own
q quit   h keymap   j/k g/G move   nothing you press can reach the shepherd                                                                              █ frozen

Actions

--read-only (or shep set lookout.allow_control false) closes the gate; it's open by default, and closed means every action key refuses with a plain sentence rather than silently doing nothing:

120×30, gate closed
shep lookout   /home/ada/.shep                                                                            6 in the flock
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     0 errored · 0 parked   floc…
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ID    NAME        STATUS           PID      RESTARTS  EXIT       CPU     MEM       UPTIME    FOLD        SMIT
   ██ FLOCK
> 2     api         online           48219    1         -          7.1%    241.0M    1h 28m    edge        -
  3     billing-r…  online           48230    0         -          0.8%    96.0M     1h 29m    -           -
  4     cron        online           48233    0         -          0.1%    8.0M      1h 31m    -           -
  5     metrics     online           48240    0         -          0.4%    11.0M     1h 32m    -           -
  0     web         online           48211    0         -          3.4%    182.0M    1h 25m    edge        -
  1     web         online           48212    0         -          2.9%    178.0M    1h 26m    edge        -
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ SHEEP 2  api   online   pid 48219   restarts 1   uptime 1h 28m   cpu 7.1%   mem 241.0M   fold edge
lambs  not read yet
out  /home/ada/.shep/logs/api-2-out.log   │   err  /home/ada/.shep/logs/api-2-err.log

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ██ BLEATS api  … 1 earlier line not shown
out  GET /healthz 200 3ms
out  GET /v1/orders 200 44ms
out  POST /v1/orders 201 88ms
out  GET /v1/orders/8821 200 9ms
out  connection pool: 14/50 in use
read-only: from --read-only or lookout.allow_control                                                           read-only

With the gate open, three keys arm an action on the selected sheep: x stops it, R restarts it, L reloads it. There's no start key; lookout only ever acts on a sheep already in the flock. Arming doesn't send anything by itself, it puts a question in the status bar naming the verb and the exact sheep:

100×14, armed
shep lookout   /home/ada/.shep                                                        6 in the flock
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     0 error…
────────────────────────────────────────────────────────────────────────────────────────────────────
  ID    NAME               STATUS           PID      RESTARTS  EXIT       CPU     MEM       UPTIME
   ██ FLOCK
> 2     api                online           48219    1         -          7.1%    241.0M    1h 28m
  3     billing-reconcil…  online           48230    0         -          0.8%    96.0M     1h 29m
  4     cron               online           48233    0         -          0.1%    8.0M      1h 31m
  5     metrics            online           48240    0         -          0.4%    11.0M     1h 32m
  0     web                online           48211    0         -          3.4%    182.0M    1h 25m
  1     web                online           48212    0         -          2.9%    178.0M    1h 26m
restart api (id 2)? enter confirms, any other key cancels                            control enabled

Enter confirms and sends; any other key cancels and does nothing else, so a stray j right after arming can't both dismiss the prompt and move the cursor. An armed prompt nobody answers expires after 10 seconds on its own. Once sent, the bar shows the request in flight until the shepherd answers, either with what it did:

100×14, accepted
shep lookout   /home/ada/.shep                                                        6 in the flock
host  load  ██░░░░░░░░ 2.31 4.10 3.88 / 10 cores     host mem  ████░░░░░░ 12.4G / 32.0G     0 error…
────────────────────────────────────────────────────────────────────────────────────────────────────
  ID    NAME               STATUS           PID      RESTARTS  EXIT       CPU     MEM       UPTIME
   ██ FLOCK
> 2     api                online           48299    2         -          -       241.0M    1h 18m
  3     billing-reconcil…  online           48230    0         -          0.8%    96.0M     1h 29m
  4     cron               online           48233    0         -          0.1%    8.0M      1h 31m
  5     metrics            online           48240    0         -          0.4%    11.0M     1h 32m
  0     web                online           48211    0         -          3.4%    182.0M    1h 25m
  1     web                online           48212    0         -          2.9%    178.0M    1h 26m
restart api (id 2): the shepherd restarted it                                        control enabled

or with its own refusal, forwarded rather than rewritten, so the sentence on screen is the daemon's, not lookout's guess at one. Besides the closed gate, arming also refuses if the link isn't live, if nothing is selected, or if another action is already out: one at a time, and the ladder is checked in that order every time.

This is a fat-finger catch, not a security boundary: lookout runs as your own process under your own uid, so the shepherd has no way to refuse a keystroke it can't tell apart from you typing shep stop yourself. Every color on screen is also redundant with text, so NO_COLOR and a 16-color terminal lose decoration, never information.

Editing config

Four panes write rather than read: settings, secrets, a sheep's Flockfile fields and a dog's own schema. They are their own chapter, Editing config in lookout, because they are a different job from reading a flock table.