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.
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.
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.
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.
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.
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 enabledBelow 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.
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.
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 … █ followingesc 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.
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.
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.
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:
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.
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:
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:
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:
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.