1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
[]
= "shep"
= "The shep binary: a process manager that keeps a flock of long-running processes alive on macOS, Linux and Windows, with logs, watch and cron restarts, and webhook alerts"
= true
= true
= true
= true
= true
= "README.md"
= ["shep", "process-manager", "supervisor", "daemon", "cli"]
= ["command-line-utilities", "os", "development-tools"]
# The docs site, not docs.rs: this crate ships a CLI, and its three [[bin]]
# targets are what somebody installs. docs.rs would render the thin library
# underneath them, which is not what a reader of this package came for.
= "https://shep-pm.com"
[]
= "shep"
= "src/lib.rs"
[[]]
= "shep"
= "src/bin/shep.rs"
# Container entrypoint aliases (spec §3). Each is three lines over the library
# above; the verb is supplied by the entry point it calls, so nothing here
# reads argv[0].
[[]]
= "shep-runtime"
= "src/bin/shep-runtime.rs"
[[]]
= "shep-dev"
= "src/bin/shep-dev.rs"
# The .deb built by `.github/workflows/release-artifacts.yml`, for the
# download-and-`dpkg -i` path documented in docs/distribution.md. That is not
# `apt install`, which needs a signed repository nobody hosts yet.
#
# Built against musl, never glibc. `depends = "$auto"` writes whatever glibc
# the builder linked against; ubuntu-latest is 24.04, so a glibc build would
# demand 2.39 and refuse to install on Debian 12 or Ubuntu 22.04. A static
# musl binary makes the question disappear, and leaves `$auto` with nothing
# to find.
#
# No systemd unit and no maintainer scripts, deliberately. `shep startup`
# renders and installs the unit when an operator asks for one, and a
# `postinst` that did it instead would write to a user's systemd state at
# `dpkg -i` time, before they have written a Flockfile.
[]
= "TurtIeSocks <TurtIeSocks@users.noreply.github.com>"
= "Copyright (c) TurtIeSocks"
# cargo-deb takes one license file. Both ship as assets below, since the
# license is "MIT OR Apache-2.0" and a reader needs to see both to choose.
= ["LICENSE-APACHE", "0"]
= "admin"
= "optional"
= "$auto"
= """shep keeps a flock of long-running processes alive: it restarts them when they die, captures what they print, and says plainly when something is wrong. It also restarts on file changes, on a cron schedule, or over a memory limit, and can alert a webhook when a process misbehaves.
This package installs the binaries only. Run `shep startup` to have systemd supervise the flock from boot; nothing here writes to systemd on your behalf."""
= [
# `target/release/` is cargo-deb's own prefix for a built binary. It
# rewrites it to the real target directory, so this is correct under
# `--target=x86_64-unknown-linux-musl` as well.
["target/release/shep", "usr/bin/", "755"],
["target/release/shep-runtime", "usr/bin/", "755"],
["target/release/shep-dev", "usr/bin/", "755"],
["README.md", "usr/share/doc/shep/README.md", "644"],
["LICENSE-MIT", "usr/share/doc/shep/LICENSE-MIT", "644"],
["LICENSE-APACHE", "usr/share/doc/shep/LICENSE-APACHE", "644"],
# Written by the release workflow before it calls cargo deb, by running
# the binary it just built. Paths are relative to this manifest, which is
# why they land in the crate directory rather than under `target/`.
["completions/shep.bash", "usr/share/bash-completion/completions/shep", "644"],
["completions/_shep", "usr/share/zsh/vendor-completions/_shep", "644"],
["completions/shep.fish", "usr/share/fish/vendor_completions.d/shep.fish", "644"],
]
[]
# `features = ["schema"]`: `shep schema` prints `shep_core::config::
# flockfile_schema_json()`, which only exists behind that feature. This
# crate names no `schemars` type of its own for it — it calls a shep-core
# function that returns a `String`.
= { = true, = ["schema"] }
# Unconditional even though only its `#[cfg(unix)]` modules (`boot`, `sys`,
# `tokio_runner`, `server`) are reachable here: the pure tier compiles on
# every target, so the platform gate belongs on our `use` sites, not on this
# dependency edge.
= true
= true
# `dog::metrics`'s host reading (`shep_host_*`). Adds zero crates to the
# tree: shep-daemon already carries `sysinfo` for its own tree sampler
# (`limits/sample.rs`), and this is the same workspace-pinned version —
# confirmed with `cargo tree -p shep-cli | grep sysinfo` showing exactly one
# resolved version, matching `Cargo.lock`'s single `sysinfo` entry both
# before and after this change.
= true
# `serve`'s basic-auth comparison, through `hmac::sign`/`hmac::verify` — see
# `serve/auth.rs`'s `credentials_match` doc comment for why it is `hmac` and
# not `constant_time`/`digest` as this phase's plan originally specified:
# `ring::constant_time` is `deprecated_constant_time` under an alias in the
# 0.17.14 this workspace resolves, and its own doc comment disclaims side-
# channel guarantees for external callers. Adds ZERO crates to the tree —
# `tokio-rustls` above already pulls this exact version in for the bark
# dog's TLS (Cargo.lock resolves one `ring`), and this crate already pays
# ring's `cc` build script on every cross-compile. Named directly rather
# than reached through `tokio_rustls::rustls`: rustls does not re-export
# ring's hmac, and a crate that uses a dependency says so.
= { = "0.17", = false }
# How many terminal columns a `char` occupies, for `output::width` (which
# every table cell is padded by) and `lookout`'s own `fit`. Both used to
# count `char`s, so a CJK name or a log line built from double-width
# characters overran its column and pushed every border after it -- see
# `output/width.rs`'s own doc for the two different width questions this
# crate asks. Adds ZERO crates to the tree: `ratatui-core` already pulls
# this exact version in for its own grapheme measurement, and `shep-core`
# reaches it through `serde-saphyr`'s `annotate-snippets` -- `Cargo.lock`
# resolves one `unicode-width`, both before and after this change. Declared
# here rather than left to feature unification with either of them, because
# a crate that uses a dependency says so.
#
# `default-features = false` drops the `cjk` feature, which exists only to
# provide `width_cjk` -- the East-Asian *Ambiguous* reading, where `▸` and
# friends measure two columns instead of one. Plain `width` is the right
# answer for a terminal nobody has told us is CJK-legacy, and it is the
# reading `lookout::view::flock::mark`'s own doc already assumes.
= { = "0.2", = false }
= { = "4", = false, = ["std", "derive", "help", "usage", "error-context", "suggestions", "env", "wrap_help"] }
# `default-features = false` is a no-op here — clap_complete's own default
# feature set is empty — kept for consistency with the workspace rule.
# `unstable-dynamic` is deliberately not enabled: dynamic completion is out
# of scope for this phase.
# Floor 4.6.0, not the crate's actual minimum: `CompletionArgs::shell` names
# `clap_complete::aot::Shell`, and the `aot` module does not exist before
# 4.6.0 (verified against the crates.io index by extracting each 4.x.0
# release's `src/lib.rs` — 4.0.0 through 4.5.0 have no `aot` module at all;
# `Shell` and `generate` sat at the crate root). `-Z minimal-versions` picks
# 4.0.0 without this pin and fails with "cannot find `aot` in `clap_complete`".
= { = "4.6.0", = false }
# `io-util` is `crate::http`'s: `BufReader`, `AsyncBufReadExt`/`AsyncReadExt`/
# `AsyncWriteExt`, and `tokio::io::duplex` for its own tests — the same
# reason shep-daemon's own hand-rolled HTTP probe (`probes/os.rs`) carries it.
# `fs` is `serve::worker`'s: `tokio::fs::File` and `tokio::io::copy` for
# streaming a served file off disk. It adds zero crates — tokio's `fs`
# feature is `spawn_blocking` over `std::fs` and pulls in no new dependency
# (confirmed: `cargo tree -p shep-cli` prints the same line count before and
# after — see Task 6's report).
= { = true, = ["rt-multi-thread", "macros", "signal", "net", "time", "sync", "io-util", "fs"] }
# `dog::bark::sinks`'s TLS transport for Discord/Slack webhooks. The maintainer's ruling
# (2026-08-12): a hand-rolled HTTP/1.1 client over `tokio-rustls`, not
# `reqwest` — see the root `Cargo.toml` entry for the full accounting,
# including what ring's own `cc` build dependency costs a cross-compile.
# `ring`/`tls12` name the crypto provider directly so `tokio-rustls`'s own
# default features (which pull in `aws_lc_rs`, which needs cmake on top of a
# C compiler) never apply.
= true
# Mozilla's compiled-in TLS root store, for the same TLS connector.
= true
# The one GLOBAL subscriber install in the workspace, in `commands::daemon`
# (shep-daemon's own `testing::capture_logs` installs a scoped one per test).
# Every feature the workspace entry names is live there: `fmt` renders,
# `env-filter` turns `[daemon] log_level` into a filter, `json` is the
# `log_json` branch, and `ansi` is colour when stderr is a terminal and
# `NO_COLOR` says nothing to the contrary.
= true
# `output::table::local_timestamp`'s rendering of `shep barks`' `WHEN`
# column: a `Bark::at_ms` (unix millis, UTC by construction) into the
# operator's own local zone. Adds zero crates to the tree — shep-core and
# shep-daemon already carry it, at this same workspace-pinned version
# (`Cargo.lock` resolves exactly one `chrono`), and the workspace entry's
# `clock` feature is what makes `Local` exist to convert into.
= true
# `bleats.rs`'s `stream.next()` now comes from `EventStream::next`, an
# inherent method shep-client provides directly — no `StreamExt` import
# needed here any more. This dependency stays for `FutureExt`: the Ctrl-C
# wiring (`tokio::signal::ctrl_c().map(|_| ())`) still needs it, and
# nothing else in this crate's dependency graph exposes it.
= true
# `Render: Serialize` needs the trait and its derive, not just the
# serializer — `serde_json` alone does not bring either.
= true
= true
# Rendering the Flockfile `shep import` writes. `parse` is unused here — the
# CLI reads Flockfiles through shep-core — but the workspace entry names both
# and a per-crate feature narrowing would be the only difference.
= true
# `commands::shep_toml`'s `ShepToml`: the one writer of `$SHEP_HOME/shep.toml`
# in this binary, edited through `toml_edit::DocumentMut` rather than a plain
# `toml::Table` so an operator's comments, key order and formatting survive a
# `shep enable`/`shep disable`. Not a workspace entry (like `clap_complete`
# above): this crate is its only user. Adds zero crates to the tree —
# `toml` 0.8 already pulls in `toml_edit` 0.22.27 as its own backing
# implementation (`Cargo.lock`), and `default-features = false` plus this
# exact feature pair matches what that pulls in already.
#
# Floor 0.22.7, not the bare "0.22" that reads: `DocumentMut` itself (the
# type this whole dependency exists for) is a 0.22.7 rename of the older
# `Document` — `-Z minimal-versions` picked 0.22.0 without this and failed
# `shep_toml.rs`'s import with `no DocumentMut in the root` (CI run
# 32023586026). The "0.22" in the prose above was never wrong about what
# ships; it was wrong about what the declared floor let `-Z minimal-versions`
# pick.
= { = "0.22.7", = false, = ["display", "parse"] }
# `commands::shep_toml`'s write path: `shep.toml` is staged in a sibling temp
# file at `0600` (mode at creation, via `Builder::permissions`), `fsync`ed and
# `rename`d over the original, the same shape shep-core's `barks::write_ring`
# uses. Promoted from `[dev-dependencies]`, where the test tier already had it:
# adds zero crates to the tree, since shep-core and shep-daemon both carry this
# same workspace-pinned version already.
= true
# `style::StyleLevel::colour()`'s consumer: the boxed renderer picks its ANSI
# escapes through `anstyle::Style`/`Color` rather than hand-rolled escape
# sequences. Declared here in the task that adds `style.rs` even though that
# module's own code does not call it yet — this crate's code starts using it
# once the renderer that reads `StyleLevel` lands. Adds zero crates to the
# tree: `Cargo.lock` already resolves exactly one `anstyle` (shep-core pulls
# it into the lockfile through its own `cfg(any())` floor-forcing block), so
# this is the first real use of an already-resolved version, not a new one.
= true
# The lookout TUI, the MCP server behind `shep whistle`, and the schema
# generator beside it. **These four were `[target.'cfg(unix)'.dependencies]`
# and are now unconditional**, and the comment they replace is worth keeping
# in mind rather than simply deleting: it argued that declaring them
# everywhere "would build a terminal stack, `crossterm_winapi` and a second
# `windows-sys` face into a binary that cannot use any of it".
#
# That was correct while the Windows build refused every verb before it could
# reach either module. It stopped being correct when the Windows tier became
# real: `src/lookout/` and `src/whistle/` both compile and run there now — a
# `crossterm_winapi` face is what makes the dashboard work on a Windows
# terminal, not dead weight — so the cost the comment was avoiding is now the
# cost of the feature. The `x86_64-pc-windows-gnu` cross-check does get
# slower, which is the price and is worth naming.
= true
= true
# The MCP server behind `shep whistle`.
= true
# The schema generator, beside it. Declared rather than reached through
# `rmcp::schemars`, because `#[derive(JsonSchema)]` in `src/whistle/` expands
# to absolute `schemars::` paths.
= true
# Two `ratatui` transitive floors, only ever read by `-Z minimal-versions`;
# see the workspace `Cargo.toml`'s comments on each.
= true
= true
[]
# `test-util` for `#[tokio::test(start_paused = true)]` — `crate::http`'s own
# "a peer that says nothing is dropped at the timeout" test, matching how
# shep-client and shep-daemon each add this feature as a dev-only override.
= { = true, = ["test-util", "rt-multi-thread"] }
# The same shep-client the `[dependencies]` entry above pulls in, asking for
# the hand-rolled daemon fakes this crate's command tests drive. A
# dev-dependency, so the feature is on for the test targets and off for the
# shipped binary.
= { = true, = ["test-support"] }
# Snapshot coverage for `--format json` payloads.
= true
# The real-binary e2e tier (`tests/cli_e2e.rs`).
= true
= true
# Task 4's property test for `output::table`, that any cell's rendered width
# matches its declared column width regardless of ANSI styling. Declared here
# in the task that adds `output::width` even though this crate's own code
# does not call it yet, same pattern as `anstyle` above.
= true
# `geteuid` (the privilege gate `shep startup` refuses on) and
# `User::from_name`/`User::from_uid` (the target user's own passwd home, which
# is NOT `$HOME` under sudo). Both are behind nix's `user` feature, which the
# workspace entry already enables. A cfg(unix) table because nix does not build
# on Windows and this crate's Windows leg compiles everything outside
# `commands/`.
#
# "fs" on top of the workspace defaults is `launch.rs`'s descriptor sweep:
# `nix::fcntl::fcntl` and `FcntlArg::F_SETFD`/`FdFlag` all live behind it. Named
# here rather than left to feature unification with shep-daemon (which enables
# it for `sys.rs`) — a crate that uses a feature says so, or it breaks the day
# the other crate stops needing it.
# `nix::fcntl::fcntl` and `FcntlArg::F_SETFD`/`FdFlag` — the inherited-fd
# sweep in `launch.rs`, which has no Windows counterpart to need.
[]
= { = true, = ["fs"] }
# A process-group assertion and a teardown Drop guard both need to signal a
# real pid; same cfg(unix) dev-only shape shep-daemon uses. The workspace
# default nix features (signal, process, user) already cover `getpgid` and
# `kill`.
[]
= true
[]
= true
# `cfg(any())` never matches any target, so nothing here is ever compiled into any real
# build — but Cargo's resolver still walks these entries when computing the dependency
# graph, including under `-Z minimal-versions`. Same mechanism shep-daemon uses for its
# own two floor pins. Here it forces `lazy_static` past the versions whose plainly
# exported `lazy_static!` breaks `sharded-slab`'s path import (root Cargo.toml's comment
# on the entry has the exact mechanism). Safe to drop once `sharded-slab` corrects its
# own declared floor.
[]
= true