# Set up Mesh LLM with an assistant

This is an agent-facing installation and setup runbook. Give this file to an
assistant running on the machine that will be the main Mesh LLM node and ask it
to follow the instructions. The assistant may operate this machine, but Mesh
LLM itself is not being granted remote-execution authority.

The intended result is a private mesh containing this machine and one or more
additional machines, followed by a practical model plan and an optional local
agent-harness configuration.

The current version of this runbook is always available at
<https://meshllm.cloud/setup-mesh>. If you lose it mid-session, re-fetch that
URL rather than relying on memory.

## Instructions for the assistant

Work interactively. Explain each material choice, ask before installing,
updating, downloading a large model, changing a service, connecting with SSH,
or launching an agent harness. Do not claim success from process startup alone:
observe the mesh, model, and inference paths that the user intends to use.

Treat this as a conditional runbook, not a script that always starts at step 1.
Determine the current state, then enter the relevant section: Mesh absent,
installed but stale, hardware unknown, remote inventory unknown, topology
known, peer missing, model loading, exact inference failing, or harness absent.
After every action, verify its success condition before advancing.

When the user must do something on another machine, first say:

- which machine and shell the command is for;
- what it will install, change, or start;
- why it is the next action; and
- what output or state will mean it worked.

Then provide exactly one complete copy/paste block for that machine. Resolve
all placeholders before showing it. A multiline shell block is acceptable when
it is one paste-and-run operation. Offer to copy it with `pbcopy`, `wl-copy`, or
PowerShell `Set-Clipboard` when available, but only after asking; always display
the same block, and warn that an enrollment block contains an invite token.

Treat the installed CLI as the authority for its version. Run relevant
`--help` commands before composing flags. Use these repository documents for
the concepts, but do not assume that a newer document describes an older
installed binary:

- [Mesh workflows](https://meshllm.cloud/MESHES.md)
- [Skippy split serving](https://meshllm.cloud/SKIPPY_SPLITS.md)
- [Agent harnesses](https://meshllm.cloud/docs/agents/)
- [Configuration](https://meshllm.cloud/docs/config-reference/)

Do not make a mesh public unless the user explicitly asks. Do not post an
invite token, credentials, host inventory, or private paths to a public service.
An ordinary private invite is connectivity material, not a strong
identity/admission policy. For an untrusted network or controlled membership,
stop and discuss the owner identity and trust-policy options in
<https://meshllm.cloud/MESHES.md> before enrolling nodes.

### 1. Ask what the user is building

Begin with a short Q&A. Ask only what is not already known, and allow the user
to say “explore this machine” when they do not know an answer.

Establish:

1. The goal: chat, coding agent, several independent models, one model split
   across machines, experimentation, or simply proving that a mesh works.
2. The number of machines and, when known, each OS, architecture, RAM/unified
   memory, GPU, GPU memory, and network location.
3. Whether the machines are on the same fast LAN or separated by the internet.
4. Whether this assistant may use an existing SSH host alias for a remote
   machine. SSH is optional and must be explicitly approved before connecting.
5. Whether the user wants temporary foreground processes first or a persistent
   service after the experiment is proven.
6. Which local agent or chat harness they want to use, if any.
7. Whether stable releases are required or the user intentionally wants a
   prerelease/development build.

Default to one additional node, a private mesh, foreground processes, and
preflighted final models. Use a small starter model only when the additional
machine cannot be inventoried before mesh creation. Do not require an agent on
the other machine.

### Pick the shortest path for the goal

This is a conditional runbook, not a linear script. Once you know the goal from
section 1, follow only the sections it needs instead of walking all eleven:

| Goal | Sections to follow |
| --- | --- |
| Local chat or coding agent on one machine you already have | 2 (install check), 3 (survey — filtered), 9 (harness), 11 (report). Skip 4–7. |
| Prove a two-node mesh works | 2, 3, 4, 5, 6, 11 |
| Several independent models across machines | 2, 3, 4, 5, 6, 7, 11 |
| One large model split across machines (advanced) | 2, 3, 4, 5, 6, 7 (split shape) + `docs/SKIPPY_SPLITS.md`, 11 |
| Persistent service after a foreground test proves out | Prove the goal first, then revisit service setup in 2 |

Diagnostics (section 8) and the journal (section 10) apply throughout. When a
single-machine harness is the whole goal, do not build enrollment blocks,
topology tables, or split doctors the user did not ask for.

### 2. Establish the installed release

On this main machine, inspect without changing anything:

```sh
command -v mesh-llm || true
mesh-llm --version 2>/dev/null || true
mesh-llm --help 2>/dev/null || true
```

Determine how the binary was installed when possible. Do not replace a source
or development build with a release binary without asking.

Inspect all resolved installations before adding another one:

```sh
type -a mesh-llm 2>/dev/null || true
```

If a working release binary already exists, update it in place rather than
creating a duplicate. If Mesh is absent, use the official per-user default
unless the user has a preferred executable directory: `~/.local/bin` on
macOS/Linux and `%LOCALAPPDATA%\mesh-llm\bin` on Windows. After installation,
verify the exact resolved path and version. A binary file left from a source
build is not an installed release merely because it exists.

Check the current stable GitHub release separately from the installed version:

```sh
curl -fsSL https://api.github.com/repos/Mesh-LLM/mesh-llm/releases/latest |
  python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])'
```

If Python is unavailable, inspect the API response with another local JSON
tool. In PowerShell use:

```powershell
(Invoke-RestMethod https://api.github.com/repos/Mesh-LLM/mesh-llm/releases/latest).tag_name
```

Compare semantic versions, ignoring a leading `v`. Do not interpret a failed
network check as proof that the installation is current. Once a node is
running, `/api/status` may also expose `version` and `latest_version`; use that
as a second signal.

If the user explicitly wants a prerelease, do not compare it with the stable
`releases/latest` endpoint. Inspect the published releases and select the first
entry where `prerelease` is true and `draft` is false:

```sh
curl -fsSL 'https://api.github.com/repos/Mesh-LLM/mesh-llm/releases?per_page=20' |
  python3 -c 'import json,sys; print(next(r["tag_name"] for r in json.load(sys.stdin) if r["prerelease"] and not r["draft"]))'
```

Use `mesh-llm update --version '<tag>'` only after the user confirms that exact
prerelease.

If Mesh is absent, offer the official installer. The Unix installer supports
Apple Silicon macOS, Linux x86_64, and Linux aarch64:

```sh
curl -fsSL https://meshllm.cloud/install.sh | bash
```

Windows x64 PowerShell:

```powershell
irm https://meshllm.cloud/install.ps1 | iex
```

The `curl ... | bash` invocation pipes stdin, so the installer cannot auto-run
the interactive setup step; it prints the setup command instead. Afterwards, open
a new shell if necessary and verify `mesh-llm --version` and `mesh-llm --help`.

On Apple Silicon macOS, the canonical Homebrew tap is also available:

```sh
brew install Mesh-LLM/tap/mesh-llm
```

Intel macOS is not currently available through Homebrew.

If an installed release is behind, explain the difference and ask before
running:

```sh
mesh-llm update
```

After an install or update, verify the version and required commands again.
If `setup`, `doctor`, or another documented command is missing, treat that as a
binary/version mismatch, not user error. Do not continue with flags the binary
does not advertise. Use the official installer/update path, with approval, to
obtain a release that contains the required surface.

Run machine setup if it has not completed:

```sh
mesh-llm setup
```

For an assistant-controlled, temporary experiment, prefer explicit
non-service setup after approval:

```sh
mesh-llm setup --yes --no-service
```

Do not install a service yet unless the user asks. A service runs
`mesh-llm serve`, which requires startup models in config; proving a foreground
topology first produces much clearer diagnostics.

### 3. Survey this machine before choosing models

Discover capabilities from Mesh rather than estimating from a product name:

```sh
mesh-llm doctor --json
mesh-llm gpus --json
mesh-llm models installed --json
mesh-llm models recommended --json
```

If `gpus --json` reports zero/blank capacity, stale values, or an incomplete
fingerprint on a machine known to have an accelerator, do not plan from it.
Explain that detection refreshes Mesh's cached hardware fingerprint, then run
with approval:

```sh
mesh-llm gpus detect --json
```

Also inspect the live help for the operations likely to be used:

```sh
mesh-llm serve --help
mesh-llm models search --help
mesh-llm models show --help
mesh-llm doctor split --help
```

Record detected backend, usable capacity, installed models, version, OS, and
warnings. On Apple Silicon, unified memory is useful model capacity, but it is
not all safely allocatable to model weights and KV cache. On discrete-GPU
systems, Mesh may report serving capacity differently from raw GPU VRAM. Use
Mesh's detected/advertised capacity and model-fit data; leave headroom for the
OS, context/KV cache, and runtime overhead.

Inventory every proposed serving machine before choosing a model whenever
possible. There are two supported preflight paths:

1. With approved SSH, run install/version checks, `doctor`, `gpus`, disk space,
   and `models installed` on the named host directly.
2. Without SSH, give the user one copy/paste inventory block that installs Mesh
   only if absent, verifies/updates only after consent, and prints a compact
   receipt containing version, hardware, free disk, and installed-model JSON.
   Ask the user to paste that receipt back.

Do not infer a runnable model from a Hugging Face cache directory name. A repo
may contain only metadata, one multipart shard, or a few layer-package files.
Prefer exact entries returned by `mesh-llm models installed --json`, inspect
their reported path and total size, and use `models show` for fit/capabilities.
Prefer complete cached models before proposing any large download.

`models installed --json` currently enumerates individual layer-package and
split-shard files as separate entries (tracked as a bug: mesh should group
these under one package ref). On a machine with cached Skippy packages this can
be dozens of rows that are not independently runnable. Filter before you show
anything to the user:

- Exclude split fragments and package internals: refs ending in `-layers` (or
  containing `/layers/` or `/shared/`), and any `layer-*.gguf`, `shared/*.gguf`,
  `metadata.gguf`, or `skippy-shard-*` path. These are only relevant when the
  goal is a split (section 7 and `docs/SKIPPY_SPLITS.md`).
- Keep complete, independently loadable GGUFs.
- For a coding-agent goal, rank the survivors by advertised `tool_use` support,
  then by fit, and confirm capability with `models show`.
- Present at most three candidates with size, capability evidence, and fit.
  Never paste the raw installed list back to the user.

If remote inventory is unavailable and the initial goal is only to create a
mesh, select a small starter model from the current catalog or local inventory.
Prefer a well-supported model that is a small fraction of local capacity and
quick to download. This is the fallback, not the default. Inspect exact
candidates instead of inventing a model id:

```sh
mesh-llm models search coding --catalog --json
mesh-llm models show '<exact-model-ref>' --json
```

For a coding-agent goal, require advertised tool-use capability where the CLI
provides it. Present the candidate, artifact size, capability evidence, and why
it fits before starting a large download.

### 4. Choose the launch path and start the private mesh

Bare `mesh-llm serve` is not an empty mesh creator in current releases: it
needs at least one configured or explicit startup model. If all machine
inventories are known, choose the final independent/replica/split topology now
and start this machine with its final model. Do not load a disposable starter
first merely to obtain an invite.

If inventories are not available, use the chosen starter as a temporary
bootstrap. Start either model in a supervised foreground terminal and use JSON
events so the assistant can capture exact values:

```sh
mesh-llm serve --model '<selected-model-ref>' --name '<main-node-name>' --log-format json
```

Do not detach a TUI process with `nohup`. Keep it in a terminal or a supervised
PTY that remains observable. `--headless` disables the embedded web UI, not the
terminal UI, and is not a general background-mode flag.

Capture the complete invite token from the structured startup event. Never ask
the user to retype it from a clipped display. Keep the token out of the setup
journal and redact it from summaries. It must appear in the generated enrollment
block because it is what allows the other node to connect.

Confirm on the main node:

```sh
curl -fsS http://127.0.0.1:3131/api/status
curl -fsS http://127.0.0.1:9337/v1/models
```

### 5. Generate one enrollment block per additional node

The other machine does not need an agent and the main machine does not need
direct access to it. Generate one copy/paste block containing install, setup,
and join. Substitute the exact invite and a unique node name. Quote values for
the target shell and never use placeholders in the final block.

For macOS or Linux:

```sh
set -eu
command -v mesh-llm || curl -fsSL https://meshllm.cloud/install.sh | bash -s -- --no-setup
MESH="$(command -v mesh-llm)"
"$MESH" setup --yes --no-service
"$MESH" doctor --json
exec "$MESH" serve --join '<complete-invite-token>' \
  --model '<exact-cached-model-ref>' --name '<node-name>' --log-format json
```

For Windows x64 PowerShell:

```powershell
$Mesh = Get-Command mesh-llm.exe -ErrorAction SilentlyContinue
if (-not $Mesh) {
  & ([scriptblock]::Create((irm https://meshllm.cloud/install.ps1))) -NoSetup
}
$MESHPATH = (Get-Command mesh-llm.exe -ErrorAction Stop).Source
& $MESHPATH setup --yes --no-service
& $MESHPATH doctor --json
& $MESHPATH serve --join '<complete-invite-token>' `
  --model '<exact-cached-model-ref>' --name '<node-name>' --log-format json
```

The templates show the preferred preflighted final-model path. Resolve the
exact cached model ref before presenting the block. Omit `--model` only when
inventory was unavailable or the user explicitly wants standby/unallocated
capacity. Neither form gives the mesh permission to run arbitrary commands on
that machine.

Tell the user that the final command remains in the foreground and should keep
running. If a block fails before the node joins, the main node cannot see its
local error. Ask for the terminal output, or offer SSH-based inspection.

If the user approved SSH, first inspect the named target rather than scanning
the network. Explain the exact remote commands, then use SSH to install, survey,
join, and observe logs. Do not copy private SSH keys, alter SSH configuration,
or connect to other discovered hosts without separate approval. For a long-lived
remote serve, use a held TTY and interactive login shell. Quote the entire
login-shell `-c` payload as one argument and verify the remote PID/ports
immediately; if it prints top-level help and exits, treat that as command
construction failure, not a Mesh failure.

### 6. Supervise joining and report state changes

Poll the main management API while the user runs each block:

```sh
curl -fsS http://127.0.0.1:3131/api/status
curl -fsS http://127.0.0.1:9337/v1/models
```

During active setup, check every few seconds and give the user a concise update
whenever a node changes from absent to connected, standby, loading, or serving.
During a long model download/load, reduce polling and report at least once a
minute. Do not spam unchanged JSON.

For each expected node, verify:

- the peer appears and remains connected;
- its hostname/node label, Mesh version, hardware/capacity, and available or
  hosted models are present when enumeration is enabled;
- the mesh id is consistent and publication remains private;
- versions are compatible (prefer the same current release for a first test);
- the expected model eventually appears in `/v1/models`;
- a real chat completion succeeds and returns text.

Use a bounded request for the end-to-end test:

```sh
curl -fsS --max-time 120 http://127.0.0.1:9337/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer mesh' \
  -d '{"model":"<exact-id-from-v1-models>","messages":[{"role":"user","content":"Reply with OK"}],"max_tokens":16}'
```

Do not call a topology healthy merely because `llama_ready` is true or a model
is listed. The completion test catches routing, backend, and split-stage
failures that status alone can miss.

Use a model-appropriate bounded output allowance. Reasoning models may consume
dozens or hundreds of tokens before emitting visible answer text. A response
that reaches the right model but ends at `finish_reason: length` with no useful
text proves transport only, not usable inference. Retry once with a reasonable
bounded allowance and inspect the full assistant message before diagnosing the
route as broken.

### 7. Plan what the machines should serve

Use this section before launch when preflight/SSH has supplied all inventories;
otherwise use it after standby nodes join and advertise theirs. Summarize the
topology in a compact table: node, OS/backend, advertised usable capacity,
network latency when known, free disk, complete installed models, current
state, and version. Then offer no more than three plans, ordered from lowest
risk to most ambitious. When planning before launch, return to section 4 after
the user chooses.

Prefer these shapes:

1. **Independent models (default).** Put one model that fits comfortably on
   each node. This gives the mesh model diversity, avoids stage latency, and
   lets exact model ids or `auto` route requests. It is usually the best first
   useful two-node test.
2. **Replicas.** Run the same independently loadable model on both nodes when
   concurrency or resilience matters more than diversity.
3. **One split model (advanced).** Use a published Skippy layer package only
   when the desired model cannot fit comfortably on one node, the combined
   usable capacity has headroom, and the node-to-node link is fast and stable.

For each candidate, query current evidence:

```sh
mesh-llm models search '<family-or-use-case>' --catalog --json
mesh-llm models show '<exact-model-ref>' --json
```

Do not equate parameter count with resident bytes. Compare the exact artifact
or package size with advertised usable capacity and leave meaningful headroom.
Prefer catalog entries and certified families over arbitrary large GGUFs.

Useful planning examples, not fixed recommendations:

- For a 128 GB Apple Silicon machine plus a 64 GB Apple Silicon machine, first
  consider a strong model that fits the larger node and a smaller, faster or
  differently capable model on the smaller node. Consider a split only when an
  exact layer package is clearly too large for 128 GB but comfortably below the
  mesh's combined usable capacity after overhead.
- For a machine above 256 GB plus a 128 GB Apple Silicon machine on a fast LAN,
  it can be reasonable to evaluate a much larger package-backed split model.
  This remains an advanced plan: inspect the package, certify it, and let the
  split doctor validate actual peer eligibility before downloading hundreds of
  gigabytes.

Do not hard-code a model recommendation from these memory figures. Catalog
contents, quants, package certification, context requirements, and runtime
support change. Show the live search/show evidence and ask the user which plan
to enact.

For independent models, the main node can add a model to its active serving
runtime with the supported local lifecycle command:

```sh
mesh-llm load '<exact-model-ref>'
```

A standby remote node is not a general remote-execution target. To change what
it serves, either use approved SSH or give the user a new exact foreground
command that restarts it with `serve --join ... --model ...`. Do not imply that
the coordinator can install or launch arbitrary models remotely.

For a split, read <https://meshllm.cloud/SKIPPY_SPLITS.md>. A layer-package model
too large for any single node auto-splits once a second eligible node joins;
`--split` only forces a split of a model that would otherwise fit. Every node
must request the same package ref. Before downloading, clear three preflight
items unique to splits:

- **Inbound reachability on each worker.** The coordinator opens stage-control
  and activation connections *into* each worker, separate from gossip. A worker
  whose host firewall blocks inbound to `mesh-llm` will gossip, show healthy
  RTT, and answer chat, yet never receive a stage
  (`stage_control_unreachable`). On macOS, allow `mesh-llm` for incoming
  connections (System Settings → Network → Firewall → Options) and disable
  stealth mode; a managed/MDM Mac needs the GUI or an MDM policy, not the CLI.
- **Coordinator decode headroom.** The planner budgets weights plus KV but
  reserves no margin for decode-time command buffers, so a node packed to its
  advertised capacity can load and then fail on the first token with an
  out-of-memory decode error. Lower `--ctx-size` (respect the model's minimum
  context) to shrink KV, and cap the coordinator with `--max-vram` to push
  layers onto other nodes.
- **A fast, stable link, brought up together.** Activations cross the network
  every token and the lane handshake aborts on a transient short read. Prefer a
  wired or stable LAN, and start the coordinator and workers close together —
  widely staggered stage readiness can wedge the handshake.

Restart the relevant nodes with the generated exact commands, then run:

```sh
mesh-llm doctor split --model-ref '<exact-layer-package-ref>' --port 3131 --json
```

Require all intended stages to be ready and a real completion to succeed. If a
worker remains “standing by for stage assignment”, the coordinator reports
ready alone, or inference hangs, treat the split as failed and collect a doctor
bundle rather than waiting indefinitely:

```sh
mesh-llm doctor split --model-ref '<exact-layer-package-ref>' \
  --port 3131 --output-dir '<diagnostic-directory>'
```

### 8. Diagnose failures from the main node

Start with evidence available locally:

```sh
mesh-llm --version
mesh-llm doctor --json
curl -fsS http://127.0.0.1:3131/api/status
curl -fsS http://127.0.0.1:9337/v1/models
```

Classify before changing anything:

- **Install/CLI mismatch:** installed version lacks a documented command or
  flag. Verify the binary path and release; update/reinstall only with approval.
- **Node never appears:** obtain the remote foreground output. Check that the
  complete token was pasted, versions satisfy mesh requirements, and the node
  can reach the internet/relay or direct peer path. Use `--bind-ip` only for an
  identified multi-interface/bridge-address problem. Do not switch to mDNS as
  a generic fix; it may require local mDNS services such as Avahi and still
  requires the invite.
- **Node joins as standby:** expected only when the enrollment block omitted
  `--model` (i.e. inventory was unavailable or the user explicitly requested
  standby capacity). If `--model` was provided, standby status means the model
  failed to start — investigate model availability, capacity, and `doctor`
  output on the remote node.
- **Model absent:** distinguish downloading, resolving, loading, insufficient
  capacity, and unsupported backend. Check local inventory and exact model ref.
- **Reported ready but inference crashes or hangs:** inspect the foreground
  logs and backend/runtime selection. A load-only success does not prove that
  the first GPU kernel or distributed stage works.
- **Download/cache failure:** inspect the paths reported by `doctor`, free
  space, permissions, `HF_HOME`, and temporary-directory settings. Do not erase
  caches as a first response.
- **Split failure:** use `doctor split`, stage/runtime status, exact package ref,
  peer latency, and a diagnostic bundle. Fall back to independent models when
  the split cannot be proven healthy. Common signatures and responses:
  - `stage_control_unreachable` while the peer is connected: the coordinator
    cannot reach a worker's stage-control. Check the worker's inbound
    firewall/stealth allowance for `mesh-llm`, not just that it is a peer.
  - `claim failed: timeout waiting for stage control response`: slow/jittery
    link. Restart both nodes together and retry.
  - `persistent downstream lane did not become ready`: the stage-to-stage lane
    handshake hit a short read (network hiccup or widely staggered readiness).
    Bring the nodes up together on a stable link.
  - Decode-time OOM (e.g. `kIOGPUCommandBufferCallbackErrorOutOfMemory`,
    `llama_decode ret=-3`) after the model reports ready: the stage was packed
    with no decode headroom. Lower `--ctx-size` and/or `--max-vram` so fewer
    layers land on it. A dying stage withdraws the whole split, so apply
    headroom on every node.
  - `split_capacity_shortfall ... below minimum valid context`: `--ctx-size` is
    under the model's floor; raise it to the reported minimum.

If the cause is remote-only, offer two choices: the user pastes the relevant
remote output, or the user authorizes SSH to the named machine. With SSH
approval, inspect version, `doctor --json`, process state, and logs. For a
systemd user service use `journalctl --user -u mesh-llm.service`; for a macOS
service, obtain the exact log paths from `mesh-llm setup --verbose` or the
launchd plist. Redact invite tokens and credentials from anything retained or
shared.

### 9. Connect a local chat or agent harness

Only after exact-model inference succeeds, inspect which harnesses exist on the
main machine:

```sh
for cmd in goose claude opencode pi; do command -v "$cmd" || true; done
```

If no suitable harness exists, offer one and explain the external download
before installing it. Goose is a self-contained CLI and its current official
stable installer is documented by the
[AAIF Goose repository](https://github.com/aaif-goose/goose):

```sh
curl -fsSL https://github.com/aaif-goose/goose/releases/download/stable/download_cli.sh | bash
```

Re-check the upstream instruction at install time, ask permission, and verify
`goose --version`; do not silently install a harness as a side effect of Mesh
setup.

Ask which installed harness the user wants. Prefer Mesh's built-in launcher,
which owns the current provider configuration:

```sh
mesh-llm goose --model '<exact-model-id>'
mesh-llm claude --model '<exact-model-id>'
mesh-llm opencode --model '<exact-model-id>'
mesh-llm pi --model '<exact-model-id>'
```

For OpenCode or Pi, `--write` can configure without launching. Check each
launcher's live `--help` before using it. Do not overwrite an existing harness
configuration without explaining the change and obtaining approval.

Choose model ids deliberately:

- start with a proven exact, tool-capable model for coding agents;
- try `auto` after exact routing is known-good and the user wants Mesh to pick;
- use the virtual `mesh` model only when multiple suitable models are healthy
  and the user explicitly wants inter-model collaboration. It is not the
  default health check or a substitute for a working exact route.

Explain how to chat manually as a universal fallback: OpenAI-compatible base
URL `http://127.0.0.1:9337/v1`, any non-empty API key, and an exact id returned
by `/v1/models`.

For a coding harness, validate tool calls rather than stopping at plain chat:

```sh
scripts/qa-agent-tool-call-reliability.py \
  --base-url http://127.0.0.1:9337/v1 \
  --models '<exact-model-id>' --attempts 2 --print-plan
```

If this repository script is not present on the user's machine, perform a
small tool-call test through the chosen harness instead of assuming it exists.

### 10. Keep a resumable, secret-free journal

Maintain `~/.mesh-llm/installation-notes.md` on the main machine, mode `0600`
where supported. This is a concise human-readable working log, not raw terminal
output. Update sections such as Goal, Machines, Installation, Inventory,
Topology, Verification, Harness, Problems, and Next action after meaningful
state changes. Store:

- timestamp and installed Mesh version;
- user goal and approved access method;
- node labels and non-secret hardware/status summaries;
- chosen topology and exact model refs;
- completed checks and their outcomes;
- harness choice and non-secret endpoint/model configuration;
- the next action or diagnosed blocker.

Never store the raw invite token, SSH credentials, API keys, owner keys, or
full unredacted command lines containing secrets. Before resuming, re-check
live process/API state rather than assuming the journal is current.

### 11. Completion report

Finish with:

- installed and latest-known Mesh versions;
- nodes expected, connected, and healthy;
- the chosen topology and why it fits;
- exact models serving and where;
- a successful inference result, including whether it was exact, `auto`, or
  `mesh` routing;
- harness configuration and the command the user should run to chat;
- any remaining warnings, especially version skew, capacity headroom, model
  download size, trust policy, or unproven split behavior.

If any required end-to-end check failed, say the mesh is partially configured,
not complete, and give the smallest next diagnostic action.

## Real-world failure references

These reports inform the checks above and may help when symptoms match:

- [Outdated binary missing setup/config commands](https://github.com/Mesh-LLM/mesh-llm/issues/961)
- [Invite token clipped in terminal output](https://github.com/Mesh-LLM/mesh-llm/issues/964)
- [Split coordinator ready while a worker stage is never activated](https://github.com/Mesh-LLM/mesh-llm/issues/951)
- [Model download resolving to a read-only path](https://github.com/Mesh-LLM/mesh-llm/issues/980)
- [First inference crashes despite successful model load on an unsupported ROCm target](https://github.com/Mesh-LLM/mesh-llm/issues/966)
- [Detailed newcomer feedback on install, mDNS, model choice, and split ergonomics](https://github.com/Mesh-LLM/mesh-llm/discussions/978)
