Skip to content

feat(cli): forkd doctor — diagnose host setup with fix hints#134

Merged
WaylandYang merged 1 commit into
mainfrom
feat/cli-doctor
May 21, 2026
Merged

feat(cli): forkd doctor — diagnose host setup with fix hints#134
WaylandYang merged 1 commit into
mainfrom
feat/cli-doctor

Conversation

@WaylandYang
Copy link
Copy Markdown
Contributor

Summary

New subcommand that runs 10 checks against host forkd-readiness and surfaces fix hints. Designed to compress the "I ran `forkd fork` and it errored mysteriously" loop into one command.

Checks:

  • platform (Linux + kernel version)
  • KVM (`/dev/kvm` presence + readability)
  • cgroup v2 (`memory.max` enforcement)
  • `ip_forward` sysctl
  • `forkd-tap0` device (presence + operstate)
  • per-child netns count (`forkd-child-*`)
  • `firecracker` binary on PATH
  • kernel image present (`vmlinux-*` in common spots)
  • snapshot dir (`$XDG_DATA_HOME/forkd/snapshots`)
  • controller daemon reachable (configurable URL + token)

Each check emits PASS / WARN / FAIL / SKIP with detail and a one-line fix hint on non-pass.

Smoke test on dev box (live)

  ✓  platform         Linux version 6.14.0-36-generic
  ✓  kvm              /dev/kvm OK
  ✓  cgroup v2        cpuset cpu io memory hugetlb pids rdma misc dmem
  ✓  ip_forward       1 (forwarding enabled)
  ⚠  tap device       forkd-tap0 (down)
                          → sudo ip link set forkd-tap0 up — or rerun scripts/host-tap.sh
  ✓  per-child netns  5 provisioned
  ✓  firecracker      /usr/local/bin/firecracker
  ⚠  kernel image     no vmlinux found in common locations
                          → curl https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.10/x86_64/vmlinux-6.1.141
  ✓  snapshot dir     /home/yangdongxu/.local/share/forkd/snapshots (64 snapshots)
  ✓  daemon           http://127.0.0.1:8889 responding

  pass=8  warn=2  fail=0  skip=0

Also verified the FAIL+hint path by running without `FORKD_TOKEN` — daemon check returns:
```
✗ daemon http://127.0.0.1:8889 HTTP 401
→ set FORKD_TOKEN to match the daemon's --token-file
```

Files

  • `crates/forkd-cli/src/doctor.rs` (new, ~280 LOC) — the module
  • `crates/forkd-cli/src/main.rs` — `mod doctor;` + `Cmd::Doctor` variant + dispatch (15 lines)

Follow-up

`forkd bench` (quick latency probe: spawn + exec + branch timing) is queued as the next CLI utility. Same shape, separate PR.

🤖 Generated with Claude Code

New subcommand that runs 10 checks against the host's forkd-readiness:

  - platform (Linux + kernel)
  - KVM (/dev/kvm presence + readability)
  - cgroup v2 (memory.max enforcement)
  - ip_forward sysctl (1 = required)
  - forkd-tap0 device (presence + operstate)
  - per-child netns count (forkd-child-*)
  - firecracker binary on PATH
  - kernel image present (vmlinux-*)
  - snapshot dir (XDG_DATA_HOME/forkd/snapshots)
  - controller daemon reachable (configurable URL + token)

Each check emits PASS / WARN / FAIL / SKIP with a one-line detail and
(on non-pass) a specific fix hint. Designed to compress the "I ran
\`forkd fork\` and got a mysterious error" loop into one command.

Safe to run unprivileged; checks that need root degrade to SKIP with
a note rather than failing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@WaylandYang WaylandYang merged commit 81839cd into main May 21, 2026
2 checks passed
@WaylandYang WaylandYang deleted the feat/cli-doctor branch May 21, 2026 05:09
WaylandYang added a commit that referenced this pull request May 21, 2026
Companion to \`forkd doctor\` (#134). Runs a representative
spawn → exec → branch(diff=true) → fanout(N) → cleanup cycle
against a live daemon and prints per-step timing. Output is
screenshot-friendly.

Use case: "is forkd actually fast on this box?" — answer in 1
command, without writing a benchmark script. Also useful for
regression checks after a daemon config change.

Example output:

  forkd bench against snapshot coding-agent-fork-prewarm-v1
    fanout n=5 per_child_netns=true

    spawn (n=1)             123 ms  sb-...-0001
    exec round-trip           8 ms  exit=0
    branch (diff=true)      287 ms  pause_ms=234 diff_physical_bytes=393216
    fanout (n=5)            142 ms  28ms/child
    cleanup                  45 ms  6 sandboxes
                              -----
    total                   605 ms

Implementation: ~210 LOC in crates/forkd-cli/src/bench.rs. Uses
ureq directly (small wrapper) instead of pulling reqwest for one
command. Subcommand wired in main.rs with --tag / --n /
--per-child-netns / --daemon-url / --daemon-token options.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WaylandYang added a commit that referenced this pull request May 21, 2026
Companion to \`forkd doctor\` (#134). Runs a representative
spawn → exec → branch(diff=true) → fanout(N) → cleanup cycle
against a live daemon and prints per-step timing. Output is
screenshot-friendly.

Use case: "is forkd actually fast on this box?" — answer in 1
command, without writing a benchmark script. Also useful for
regression checks after a daemon config change.

Example output:

  forkd bench against snapshot coding-agent-fork-prewarm-v1
    fanout n=5 per_child_netns=true

    spawn (n=1)             123 ms  sb-...-0001
    exec round-trip           8 ms  exit=0
    branch (diff=true)      287 ms  pause_ms=234 diff_physical_bytes=393216
    fanout (n=5)            142 ms  28ms/child
    cleanup                  45 ms  6 sandboxes
                              -----
    total                   605 ms

Implementation: ~210 LOC in crates/forkd-cli/src/bench.rs. Uses
ureq directly (small wrapper) instead of pulling reqwest for one
command. Subcommand wired in main.rs with --tag / --n /
--per-child-netns / --daemon-url / --daemon-token options.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WaylandYang added a commit that referenced this pull request May 21, 2026
Companion to \`forkd doctor\` (#134). Runs a representative
spawn → exec → branch(diff=true) → fanout(N) → cleanup cycle
against a live daemon and prints per-step timing. Output is
screenshot-friendly.

Use case: "is forkd actually fast on this box?" — answer in 1
command, without writing a benchmark script. Also useful for
regression checks after a daemon config change.

Example output:

  forkd bench against snapshot coding-agent-fork-prewarm-v1
    fanout n=5 per_child_netns=true

    spawn (n=1)             123 ms  sb-...-0001
    exec round-trip           8 ms  exit=0
    branch (diff=true)      287 ms  pause_ms=234 diff_physical_bytes=393216
    fanout (n=5)            142 ms  28ms/child
    cleanup                  45 ms  6 sandboxes
                              -----
    total                   605 ms

Implementation: ~210 LOC in crates/forkd-cli/src/bench.rs. Uses
ureq directly (small wrapper) instead of pulling reqwest for one
command. Subcommand wired in main.rs with --tag / --n /
--per-child-netns / --daemon-url / --daemon-token options.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WaylandYang added a commit that referenced this pull request May 21, 2026
Companion to \`forkd doctor\` (#134). Runs a representative
spawn → exec → branch(diff=true) → fanout(N) → cleanup cycle
against a live daemon and prints per-step timing. Output is
screenshot-friendly.

Use case: "is forkd actually fast on this box?" — answer in 1
command, without writing a benchmark script. Also useful for
regression checks after a daemon config change.

Example output:

  forkd bench against snapshot coding-agent-fork-prewarm-v1
    fanout n=5 per_child_netns=true

    spawn (n=1)             123 ms  sb-...-0001
    exec round-trip           8 ms  exit=0
    branch (diff=true)      287 ms  pause_ms=234 diff_physical_bytes=393216
    fanout (n=5)            142 ms  28ms/child
    cleanup                  45 ms  6 sandboxes
                              -----
    total                   605 ms

Implementation: ~210 LOC in crates/forkd-cli/src/bench.rs. Uses
ureq directly (small wrapper) instead of pulling reqwest for one
command. Subcommand wired in main.rs with --tag / --n /
--per-child-netns / --daemon-url / --daemon-token options.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WaylandYang added a commit that referenced this pull request May 21, 2026
…start (#137)

The 3 CLI commands shipped 2026-05-21 (#134, #135, #136) collapse the
new-user setup from 4 hand-pasted commands to a single \`forkd doctor\`
+ \`forkd from-image\` flow. Surface this prominently in Quick start so
visitors landing from a Twitter / blog link see the modern story.

- README.md: new \"Confirm your host is ready\" subsection leads with
  \`forkd doctor\`. New \"From a Docker image (one command)\" subsection
  shows \`forkd from-image python:3.12-slim --tag py-numpy\`. New
  \"Probe your install's latency\" subsection shows \`forkd bench\` with
  example output.
- README-zh.md: parallel sections in Chinese.

The original Hub-pull, from-source, and multi-child-fork-out sections
are unchanged — those audiences still need them. New subsections come
first since they're the most-likely user path.

No code changes.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WaylandYang added a commit that referenced this pull request May 21, 2026
* docs: feature \`forkd doctor\` / \`from-image\` / \`bench\` in Quick start

The 3 CLI commands shipped 2026-05-21 (#134, #135, #136) collapse the
new-user setup from 4 hand-pasted commands to a single \`forkd doctor\`
+ \`forkd from-image\` flow. Surface this prominently in Quick start so
visitors landing from a Twitter / blog link see the modern story.

- README.md: new \"Confirm your host is ready\" subsection leads with
  \`forkd doctor\`. New \"From a Docker image (one command)\" subsection
  shows \`forkd from-image python:3.12-slim --tag py-numpy\`. New
  \"Probe your install's latency\" subsection shows \`forkd bench\` with
  example output.
- README-zh.md: parallel sections in Chinese.

The original Hub-pull, from-source, and multi-child-fork-out sections
are unchanged — those audiences still need them. New subsections come
first since they're the most-likely user path.

No code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cli): \`forkd ls\` + \`forkd kill\` — direct sandbox lifecycle

Two small subcommands that wrap GET /v1/sandboxes and DELETE
/v1/sandboxes/:id so users don't have to hand-write curl. Same
ergonomics as \`docker ps\` / \`docker rm\`.

  forkd ls
    ID                    SNAPSHOT                       PID       NETNS           GUEST_ADDR
    sb-6a0e8d4f-0023      coding-agent-fork-prewarm-v1   123456    forkd-child-1   10.42.0.2:8888
    sb-6a0e8d50-0024      speculative-1234               123457    forkd-child-2   10.42.0.2:8888
    ...
    2 sandboxes

  forkd kill sb-6a0e8d4f-0023
    ✓ sb-6a0e8d4f-0023

  forkd kill --all
  forkd kill --tag speculative-1234

Implementation in crates/forkd-cli/src/sandbox.rs (~170 LOC), wired
in main.rs as Cmd::Ls / Cmd::Kill. Reads FORKD_URL / FORKD_TOKEN
from env like the other daemon-talking commands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant