Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
7b27f79
feat(desktop): add the core crate and a fixture-driven app window (HA…
roodboi Nov 14, 2026
60d0e01
fix(desktop): read native projects with the native backend and treat …
roodboi Nov 14, 2026
4643e7b
fix(desktop): accept null collections, bound pipe draining by the tim…
roodboi Nov 14, 2026
bc7f004
fix(desktop): show exit-0 services as exited with unknown role, and l…
roodboi Nov 14, 2026
cc2677d
fix(desktop): keep startup failure detail, make Open single-flight, h…
roodboi Nov 14, 2026
9d32d82
fix(desktop): validate the CLI projects payload, show native services…
roodboi Nov 14, 2026
48fb07c
fix(desktop): trust hackd only from a private directory and a same-us…
roodboi Nov 14, 2026
7bd5b48
docs(desktop): correct the native view-only note
roodboi Nov 14, 2026
a1144a7
fix(desktop): clear a failed start only on evidence, and treat any fa…
roodboi Nov 14, 2026
0518c95
fix(desktop): scroll the sidebar project list between the fixed heade…
roodboi Nov 14, 2026
cec136c
Merge remote-tracking branch 'origin/next' into feat/desktop-app-core
roodboi Nov 14, 2026
c814cb0
docs(desktop): render all 15 fixture states in the screenshot boards
roodboi Nov 14, 2026
fbeb19d
feat(desktop): add the Doctor view (HACK-2214)
roodboi Nov 14, 2026
a6c423a
fix(desktop): do not count never-created services (inactive profiles)…
roodboi Nov 14, 2026
dab34b4
fix(desktop): scroll the detail content below a fixed header
roodboi Nov 14, 2026
a7a3905
fix(desktop): address Doctor view review (scoping, exact commands, co…
roodboi Nov 14, 2026
d5704c2
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
669b92b
test(desktop): keep the Doctor fixture consistent with the status bar…
roodboi Nov 14, 2026
87bafb3
fix(desktop): Doctor review round 2 (global secrets hygiene, target i…
roodboi Nov 14, 2026
68ae83a
fix(desktop): run hack by absolute path found on absolute PATH entrie…
roodboi Nov 14, 2026
408fb6e
fix(desktop): run hack by absolute path found on absolute PATH entrie…
roodboi Nov 14, 2026
c35832a
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
127ac3c
fix(desktop): act only on project roots the user owns
roodboi Nov 14, 2026
1608e4a
fix(desktop): validate the project root's path chain, marker and conf…
roodboi Nov 14, 2026
f47747e
fix(desktop): show the Doctor pane when no projects are registered
roodboi Nov 14, 2026
89a1af9
fix(desktop): give CLI children a PATH with only absolute entries
roodboi Nov 14, 2026
647e10d
fix(desktop): absolute-only PATH for CLI children; prefer a private d…
roodboi Nov 14, 2026
b7ee630
fix(desktop): require the CLI to select exactly the validated root, a…
roodboi Nov 14, 2026
cce493d
fix(desktop): never hand children an empty PATH
roodboi Nov 14, 2026
ced1873
fix(desktop): never hand children an empty PATH
roodboi Nov 14, 2026
7230198
fix(desktop): show Doctor as unknown until it has run
roodboi Nov 14, 2026
2eb7f7c
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
3a0cd21
fix(desktop): show Doctor when the project list could not be read
roodboi Nov 14, 2026
630d6cb
fix(desktop): on timeout, SIGTERM the CLI's group and wait before SIG…
roodboi Nov 14, 2026
569056f
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
5e0031e
fix(desktop): keep Doctor's SIGTERM grace inside its overall time budget
roodboi Nov 14, 2026
f7af68e
fix(desktop): let the CLI own lifecycle timeouts instead of cutting t…
roodboi Nov 14, 2026
749a7d3
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
c1dde2b
fix(desktop): never terminate lifecycle commands from the app; warn a…
roodboi Nov 14, 2026
3d770de
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
bacd538
fix(desktop): prefer the CLI's named remedy for daemon findings
roodboi Nov 14, 2026
c24bba2
fix(desktop): build and test on macOS, and add a headless live-state …
roodboi Nov 14, 2026
5bf507b
Merge remote-tracking branch 'origin/next' into feat/desktop-app-core
roodboi Nov 14, 2026
cbc43ef
test(desktop): never count a missing /proc as proof a process exited
roodboi Nov 14, 2026
263d04e
fix(desktop): drain CLI output pipes within bounded memory
roodboi Nov 14, 2026
efd92f8
fix(desktop): retry interrupted pipe reads and report other read fail…
roodboi Nov 14, 2026
6461b2c
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
adde344
fix(desktop): copy only allowlisted Doctor remedies, quote the projec…
roodboi Nov 14, 2026
db572a6
fix(desktop): Doctor never reports all clear for unchecked projects
roodboi Nov 14, 2026
db1d4c6
Merge remote-tracking branch 'origin/next' into HEAD
roodboi Nov 14, 2026
b341b32
fix(desktop): align the minimum toolchain and isolate state-reader tests
roodboi Nov 14, 2026
018815f
fix(desktop): keep a global sessions mux finding from the machine run
roodboi Nov 14, 2026
63298d3
Merge remote-tracking branch 'origin/feat/desktop-app-core' into feat…
roodboi Nov 14, 2026
192f0aa
Merge remote-tracking branch 'origin/feat/desktop-app-core' into HEAD
roodboi Nov 14, 2026
a9f6922
fix(desktop): show the new local ca trust check as one machine certif…
roodboi Nov 14, 2026
28033a3
Merge remote-tracking branch 'origin/feat/desktop-app-doctor' into HEAD
roodboi Nov 14, 2026
14f1675
fix(desktop): qualify Doctor remedies and incomplete coverage
roodboi Nov 14, 2026
71376d8
fix(desktop): preserve failure evidence and refuse registry-writing p…
roodboi Nov 14, 2026
fc780bf
fix(desktop): reject stale and unproven Doctor coverage
roodboi Nov 14, 2026
e9dfcd7
merge: integrate desktop core refresh admission fixes
roodboi Nov 14, 2026
009955c
fix(desktop): bound Doctor remedies and preserve unchecked targets
roodboi Nov 14, 2026
72ae2a5
fix(desktop): mark stale observations unknown and offer daemon recovery
roodboi Nov 14, 2026
4bcb105
merge: integrate unavailable daemon state and explicit recovery
roodboi Nov 14, 2026
d39223e
fix(desktop): detect failed exits across every service replica
roodboi Nov 14, 2026
45b34cd
Merge commit 'd39223e5b352e5e7a81cb28683378948e6a5d13b' into HEAD
roodboi Nov 14, 2026
e826dcb
fix(desktop): retain adopted diagnostics and constrain Git probes
roodboi Nov 14, 2026
06e436f
merge: integrate desktop core from next and preserve Doctor admission
roodboi Nov 14, 2026
4d4f13a
test(desktop): warm Doctor fixtures before bounded probes
roodboi Nov 14, 2026
cf06007
fix(desktop): exclude writable tool paths from Doctor probes
roodboi Nov 14, 2026
c71bf6f
feat(desktop): expose authored Doctor remedy arguments
roodboi Nov 14, 2026
7876ddc
fix(desktop): preserve native diagnostics and validate tool targets
roodboi Nov 14, 2026
70eea9c
fix(desktop): retain trusted Homebrew tools beside unsafe entries
roodboi Nov 14, 2026
9846c52
Merge remote-tracking branch 'origin/next' into HEAD
roodboi Nov 14, 2026
8b2affe
test(desktop): isolate Homebrew shim paths from macOS temp aliases
roodboi Nov 14, 2026
7a72470
fix(desktop): validate Docker discovery and exact native roots
roodboi Nov 14, 2026
89ec988
fix(desktop): preserve sticky ancestor semantics through plugin aliases
roodboi Nov 14, 2026
34bd957
fix(desktop): validate Doctor tool aliases and ACLs
roodboi Nov 14, 2026
de2e9b6
fix(desktop): preflight absolute Doctor helpers
roodboi Nov 14, 2026
453becd
fix(desktop): retain bounded Doctor tool coverage
roodboi Nov 14, 2026
61c53de
fix(desktop): scope Doctor tool coverage and ACL reads
roodboi Nov 14, 2026
edd90ca
fix(desktop): preserve independent certificate findings
roodboi Nov 14, 2026
f7cd612
fix(desktop): retain validated Doctor PATH priority
roodboi Nov 14, 2026
ac7ba3e
fix(desktop): audit Doctor read tool dependencies
roodboi Nov 14, 2026
40dd784
test(desktop): isolate Doctor PATH coverage preflight
roodboi Nov 14, 2026
20a188b
merge: integrate next into the Doctor view
roodboi Nov 14, 2026
357e4a5
merge: integrate next into the Doctor view
roodboi Nov 15, 2026
5721af3
fix(desktop): stop naming an internal request id on Doctor cards
roodboi Nov 15, 2026
8e219bc
fix(desktop): require a trusted interpreter for script tools in Doctor
roodboi Nov 15, 2026
0441381
fix(desktop): check #! arguments as well as the interpreter in Doctor
roodboi Nov 15, 2026
2e51177
fix(desktop): accept only literal #! arguments for Doctor script tools
roodboi Nov 15, 2026
9fe0a46
fix(desktop): fail closed on renamed env and unknown #! options in Do…
roodboi Nov 15, 2026
58036d9
fix(desktop): accept only two #! forms for Doctor script tools
roodboi Nov 15, 2026
c07653f
fix(desktop): apply the #! interpreter rule to Doctor's Mutagen helper
roodboi Nov 15, 2026
57aab28
refactor(desktop): make the #! env form depend on the private wrapper…
roodboi Nov 15, 2026
e5f7f81
merge: integrate next (#835, #831) into the Doctor view
roodboi Nov 15, 2026
fec06d8
fix(desktop): check Docker plugin and native runtime interpreters bef…
roodboi Nov 15, 2026
5f3e3d5
fix(desktop): strip interpreter startup variables from Doctor's proce…
roodboi Nov 15, 2026
8432a94
fix(desktop): drop every LD_ and DYLD_ loader variable from Doctor runs
roodboi Nov 15, 2026
7fb0940
test(desktop): check loader-variable removal on the Command, not thro…
roodboi Nov 15, 2026
583cf3b
merge: integrate next (Env tab #818, #802, #860) into the Doctor view
roodboi Nov 15, 2026
3ef8348
fix(desktop): keep Doctor's automatic Docker probes on a local socket
roodboi Nov 15, 2026
c918098
fix(desktop): read the Docker context Docker itself would load, witho…
roodboi Nov 15, 2026
b523a61
fix(desktop): reject Docker context metadata with ACL write grants
roodboi Nov 15, 2026
3d0d005
fix(desktop): refuse unreadable PATH tools instead of treating them a…
roodboi Nov 15, 2026
0883747
fix(desktop): give Doctor's private PATH a trusted dirname for candid…
roodboi Nov 15, 2026
149047b
fix(desktop): check and pin Doctor's native runtime fallback
roodboi Nov 15, 2026
aa8c30e
fix(desktop): keep Doctor's unavailable native pin unforgeable
roodboi Nov 15, 2026
dd809cf
Merge remote-tracking branch 'origin/next' into HEAD
roodboi Nov 15, 2026
868284d
docs(desktop): keep one accurate list of what is not built yet
roodboi Nov 15, 2026
0dcc232
docs(desktop): state that Doctor does not validate tool library load …
roodboi Nov 15, 2026
bca7e9c
fix(desktop): remove Bun runtime selectors from Doctor's CLI runs
roodboi Nov 15, 2026
85c980b
fix(desktop): check the lifecycle supervisor beside a compiled CLI be…
roodboi Nov 15, 2026
4eb4eee
fix(desktop): read a Doctor remedy even when diagnostic text follows it
roodboi Nov 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/desktop/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions apps/desktop/DOCTOR_TOOLS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Doctor tool coverage audit

The fixed table in `core/src/doctor_tools.rs` attributes filtered PATH tools to
read-only CLI checks. It is not inferred from UI area names, repair recipes or
what might normally be installed together. This audit follows `doctor.ts` and
its imported read helpers at this PR's CLI base. Update it when those calls
change. A listed transitive dependency can be conditional; it does not prove
that every branch executed the helper. The coverage card always identifies the
unchecked tool; only listed dependent check IDs can be downgraded.

| PATH tool | Actual read call and affected check IDs |
| --- | --- |
| bun, docker, brew, dnsmasq, mkcert, tmux, go, caddy, asdf | `doctor.ts::checkTool` looks up the named executable. Only its own tool-presence row (including optional/sessions suffixes) follows from this lookup. Docker's additional dependencies are below. |
| gum, fzf | `checkOptionalGum` / `checkOptionalFzf` call the UI path resolvers (override, bundled path, then PATH); only their optional presence rows. No Doctor read launches these UI tools. |
| mutagen | `checkMutagenBinary` resolves override/managed/PATH selection and runs `mutagen version`; only `mutagen`. Absolute candidates have a separate desktop preflight. |
| docker | `checkDockerRunning` runs `info`; `checkDockerNetwork` / `checkIngressSubnet` run network inspect (`network:*`); `checkProxyNetwork` -> `inspectProxyNetwork` runs ps/inspect (`proxy network`); `checkCoreDnsForwarding` -> `resolveCoreDnsServer` runs ps/inspect (`coredns forwarding`); `checkCaddyLocalCa` and `inspectProxyLoopback` -> `inspectCaddyCaIdentity` run ps/inspect/cp (`caddy local ca`, `proxy ports`); `checkCaddyHostMapping` -> `resolveGlobalCaddyIp` runs ps/inspect (`caddy hosts`). |
| docker, conditional | `checkHackLocalDns` calls `resolveGlobalCaddyIp` only for a nonstandard DNS answer (`dns:hack.local` and custom `dns:*`, excluding the separate lookup-only `dns:hack` / `dns:hack.gy` checks). `checkMacDnsmasqConfigForDomain` falls back to `resolveGlobalCaddyIp` for a dynamic address (`dnsmasq.conf:*`). |
| docker, runtime reads | `checkProjectRuntimeHygiene`, `checkWorktreeInstanceCollisions`, and lifecycle hygiene's active-Compose-project read call `readRuntimeProjects` -> docker ps/inspect (`runtime hygiene`, `worktree instances`, `lifecycle hygiene`). |
| git | `checkEnvKeyAtRoot` -> `doctor-env-key` runs ls-files/check-ignore (`env key`); `inspectWorktreeSecretKeys` -> `listGitWorktrees` (`worktree keys`); `doctor-generated-files` runs ls-files/check-ignore/rev-parse (`generated files`, `hack gitignore`); `inspectSecretsHygiene` runs git tracking/ignore queries (`secrets hygiene`); secret integrity and env materialization read shared/primary checkout inputs via `project-env-config`/`git-worktree`, and file-store key tracking via `secret-store` (`secret integrity`, `env materialization`). |
| security | `checkMacHostTlsTrust`, `checkSupersededCaddyRoots` and native `inspectNativeCaTrust` call `mac-ca-trust` -> security find-certificate/verify-cert (`host tls trust`, `superseded caddy roots`, `native root trust`, `native https owner`, `native https route`). Native owner/route results depend on the combined owner/trust observation. |
| brew | `checkMacDnsmasqConfigForDomain` runs `brew --prefix` (`dnsmasq.conf:*`); `createNativeHackLocalRouteDependencies` resolves the prefix the same way (`native hack.local route`). |
| systemctl | `resolveSystemdSummary` -> `readSystemdServiceStatus` probes/shows the user service (`daemon service`); Linux DNS host probes resolved status/version/unit reload (`linux dns route`). |
| loginctl | `readSystemdServiceStatus` -> `readLingerStatus` (`daemon service`). |
| ip, resolvectl | `inspectLinuxGlobalDns` -> `inspectLinuxDns` -> Linux DNS host reads link addresses and per-link resolver settings (`linux dns route`). |
| tmux, zellij | Lifecycle hygiene enumerates mux sessions; runtime project liveness reads tmux windows or zellij sessions (`lifecycle hygiene`, `runtime hygiene`, `worktree instances`). `sessions mux` separately looks up tmux only. |
| ps | Lifecycle hygiene -> `readProcessIdentities` spawns ps for process identity (`lifecycle hygiene`). Native DNS observation uses absolute `/bin/ps`, not this PATH selection. |
| lsof, ss | `inspectProxyLoopback` calls `listHostTcpListeners` only after a failed authenticated port probe, for extra listener detail. Keep the verified `proxy ports` warning and guidance; record unavailable auxiliary coverage on the one tool card, without downgrading the result. |

## Explicit independent checks

- `compose networks`: `checkComposeNetworkHygiene` reads/parses Compose YAML and
calls `analyzeComposeNetworkHygiene`. It does not invoke Docker.
- `compose backend features`: `checkComposeBackendFeatures` reads Compose and
adaptation files through `readComposeBackendReading`; pure feature decisions.
- `compose env at rest`: reads override/adoption files and reports stored literals;
no Docker read. `grafana` reads route configuration and uses Bun fetch.
- `native source size` invokes the selected native runtime metadata measurement;
it does not invoke PATH Git. Native owner/route runtime calls are not PATH ps,
lsof or caddy executions. Repairs (`sudo`, native CA repair, cache flushing)
are outside the read-only Doctor probe and never justify table entries.
- `dnsmasq:53` uses `queryDnsARecord` (UDP), not dnsmasq/dig/lsof. `resolver:*`
reads files. Native host DNS uses Node lookup. No read path invokes dig,
scutil, mkcert or openssl for these checks; they have no inferred dependency.
- `local ca trust` uses Node certificate parsing/files plus its own fixed vetted
helper PATH / absolute NSS resolver; filtering the desktop PATH does not
establish anything about those helpers. The separate NSS guard is an open
prerequisite; this table is not a mitigation for it.
- Project selection, env format/overlay file diagnostics, generated static
guidance and certificate parsing remain independent unless the exact read
helper above names a tool call. Shared/primary env inspection can query Git;
this does not make the entire Env or Project files area dependent on Git.

No entry widens ownership, mode, ACL or symlink trust. No NSS profile discovery,
credential configuration copy, environment switch or runtime side effect is
introduced by this table.
140 changes: 134 additions & 6 deletions apps/desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,140 @@ engine-down, error / cli-missing, many-projects, stopped / starting, running,
degraded / stopping, failed-start, needs-attention):
[light](screenshots/fixtures-light.png), [dark](screenshots/fixtures-dark.png).

## Doctor

The sidebar Doctor row reads `hack doctor --json` once outside all projects and
once per registered checkout. Machine checks are deduplicated by check id;
project findings link from the project header. Unknown statuses, failed reads
and projects not reached within the 90-second refresh budget remain unchecked.
Checks refresh on opening the pane and every ten minutes while it is visible.
Stale results cannot show an all-clear message or green Doctor indicator.
Missing registry rows remain unchecked and are never probed. Compose coverage
requires a report identifying that exact checkout's Hack directory; native
coverage requires exact root selection and unchanged marker identity before and
after the read, plus a known project-only check. A machine-only or ancestor-project
report cannot establish Compose project coverage.
Machine diagnostics remain visible even when a checkout cannot be verified.

Cards group findings by area and offer one copied command or documentation link.
Project commands include the checkout path. The app never runs a Doctor fix:
one-click fixes remain blocked on the CLI's explicit safe-fix metadata (R7a/R7b).
The probes do not use `hack global status`, which can start Docker.
Offscreen Doctor evidence: [light](screenshots/doctor-light.png) and
[dark](screenshots/doctor-dark.png).

For integrations, `Card.authored_remedy` exposes an `AuthoredRemedy`: `argv()`
returns authored arguments without an executable or environment, including each
project root as one lossless argument. Bind these arguments to the integration's
selected, checked CLI context and retain its action gates. Do not parse or execute
the copy display string. Standalone Doctor's copy display names `hack` from the
user's terminal PATH; it does not select a candidate CLI or home. A recipe grants
no permission to run a fix, and Doctor has no dependency on Setup's context types.

Doctor accepts owner-writable tool directories, including a normal user-owned
`/opt/homebrew/bin` with mode `0755`. A PATH directory, each selected executable,
every intermediate symlink target and canonical ancestor must be owned by this user
or root, with no group/other-write bits. A root-owned sticky ancestor such as
`/tmp` is allowed above a private entry; a shared writable PATH entry itself is
not. An unsafe executable is excluded individually, so it does not hide safe
siblings such as Git. Foreign-owned executable targets are excluded too. A
script tool's `#!` interpreter must pass the same rules (recursively); a script
whose interpreter lives in a shared writable directory is excluded and its
checks become Unknown. Only two `#!` forms are accepted: a trusted interpreter
with no arguments, or exactly `#!/usr/bin/env <name>` (env identified by its
real path), whose name resolves only through the private wrapper PATH. Any
other form, including shell flags such as `-e`, makes that tool Unknown. A tool
this user cannot read is not used either: its `#!` line cannot be checked, yet
the kernel would still follow it. macOS ACL mutation grants
(write/append/delete, child deletion, attributes, security or owner changes) are
refused for any principal; deny/read-only entries remain supported. Linux access
ACLs are conservatively refused. Unreadable ACL metadata is untrusted. The same
ACL policy covers Docker discovery/config paths and their link targets. This
may refuse owner-only ACL grants; it never attempts to infer group membership
or effective ACL ordering. Native ACL fixtures need hosted macOS qualification. Darwin ACL inspection does not require executable read access. NULL/ENOENT
counts as absent ACL only when pre/post link and target metadata show the path
still exists unchanged; missing, changed or unreadable metadata remains
untrusted. These observations cannot lock out same-user/root ABA changes.
Doctor checks each tool's executable and its `#!` interpreter chain, not the
shared libraries a compiled tool loads. Library search paths embedded in a binary
(ELF `RUNPATH`/`RPATH`, Mach-O `LC_RPATH` and `@rpath`/`@loader_path` load
commands), the system loader configuration and the dyld shared cache are not
validated; inherited loader variables are removed, but a library in another
user's writable directory named by the binary itself would still load.

Each refresh creates one private temporary PATH of wrappers that exec the checked
absolute tool paths, preserving normal shim script locations and nested lookup.
Wrappers contain checked tool paths only, not credentials or injected context
values, and their temporary files are removed afterward.
Scans deduplicate absolute directories (including canonical aliases) before the
64-directory bound and inspect at most 8,192 entries. Reaching either bound
keeps already checked tools and runs Doctor with explicit incomplete coverage;
only checks dependent on filtered or unsearched Doctor tools become Unknown,
so those tools cannot look missing or healthy. One coverage card names the
unchecked tools; independently established checks keep their result. The fixed
[Doctor tool/dependency audit](DOCTOR_TOOLS.md) excludes unrelated executables
and documents actual read calls and file-only exclusions. For a scan cutoff
or unreadable directory, tools already checked remain available and only
unsearched Doctor tools receive incomplete coverage.
A root:admin group-writable `/Applications` or app-bundle target is refused too;
this preview does not widen trust for Docker Desktop or OrbStack. The lead's
Mac pass must qualify these layouts; a skip is not proof Docker is missing. This is an ownership/permission boundary,
not a sandbox or content/signature check: trusted tools, their plugins and
same-user/root updates remain trusted. Real macOS/Homebrew behavior still needs
the combined Mac pass; the layout and nested shims are tested with owned fixtures.
Those fixtures use only a temporary Homebrew-style tree and an owned symlink
alias; expected shim paths come from the fixture's canonical root, never from
the runner's real Homebrew installation or filesystem modes.

The fixed absolute Mutagen override (`HACK_MUTAGEN_PATH`) or managed
`HACK_HOME/bin/mutagen` candidate, an absolute `HACK_NATIVE_BINARY` (or, when it
is unset, the `hack-native` the CLI would take from beside its own executable)
and every discovered Docker plugin are checked before Doctor too, including the same `#!`
interpreter rule (the native runtime does not get the `env` form); Doctor's
runs also remove inherited interpreter startup and preload variables
(`BASH_ENV`, `ENV`, `SHELLOPTS`, `PS4`, exported Bash functions, Perl, Python,
Ruby and Node options and paths, the Bun runtime's `BUN_OPTIONS`, `BUN_BE_BUN`,
`BUN_CONFIG_*` and `BUN_INSPECT*` (the CLI is a Bun-compiled binary), and every
`LD_*` and `DYLD_*` loader variable), so a checked tool script cannot be made to load code from the
environment; missing candidates
require protected ancestry, and relative/unprovable selections are refused.
For a compiled CLI, the `hack-lifecycle-supervisor` companion beside it (which
Doctor's project checks run by absolute path for projects using the supervisor
runner, ignoring any selector) must pass the same rule; an unsafe one refuses
Doctor. When the CLI is a `#!` launcher, the supervisor it would finally run cannot
be located or checked by the app.
Doctor's runs pin the checked native runtime through `HACK_NATIVE_BINARY`. When the
CLI is a `#!` launcher, the executable it finally runs (and so its fallback
directory) cannot be known, so Doctor pins an absent path inside its private
wrapper directory and native checks report the runtime as unavailable; set
`HACK_NATIVE_BINARY` to check a bundled runtime in that layout.
Docker plugin preflight preserves HOME, DOCKER_CONFIG and DOCKER_CONTEXT. Doctor's
automatic checks only use a local Docker socket: a non-`unix://` `DOCKER_HOST`,
or a selected context whose endpoint is remote (for example `ssh://`) or cannot
be found, refuses the probe, because Docker would start programs such as `ssh`
and its configured helpers that Doctor does not inspect. It
checks the selected config/plugin directories, configured `cliPluginsExtraDirs`,
the four standard Unix system plugin directories and plugin executable/symlink
targets with the same owner/mode/ancestor rule. It streams only the public extra
directory list from config JSON; credentials and context contents are ignored,
never retained, logged, copied or rewritten. Config parsing is bounded to 1 MiB;
plugin inspection to 64 directories and 8,192 entries. Unsafe or unprovable
discovery produces CouldNotCheck with a reason before the Doctor CLI/Docker probe.

This supports the documented standard Unix Docker CLI discovery model, not
opaque/custom discovery. A nonempty `DOCKER_CLI_PLUGIN_EXTRA_DIRS`, relative
discovery path or unsupported/unreadable config is conservatively refused,
without switching to another context or home. Custom/bundled discovery and real
Mac behavior still require reviewer assessment and the combined Mac pass; this
is not a general Docker sandbox. Native pre/post marker witnesses are read-only
observations, not locks against concurrent same-user changes.

## What v0 does not do yet

The Activity tab, CPU and memory, onboarding, always-on, daemon actions and
upgrades come in later units. The design and the CLI/daemon requests they need
are in `DESIGN.md` (PR #800).

## Logs tab

`hack logs --json` for the selected project, following by default:
Expand All @@ -108,12 +242,6 @@ reads for observed stopped projects include that state alongside the stream stat
The paused fixture also demonstrates the overflow count. Pause has no timeout;
following resumes through Resume, not automatically when scrolling to the bottom.

## Not yet

The Activity tab, CPU and memory, onboarding, always-on, daemon actions and
upgrades come in later units. The design and the CLI/daemon requests they
need are in `DESIGN.md`.

## Env tab (names only)

Env reads `hack env list --json --path <root>` on tab open, project selection,
Expand Down
Loading
Loading