# Diagnosing Connectivity

`swarmfile-doctor` checks that a hub, or your own [community server](https://swarmfile.com/docs/guides/self-hosting), is reachable and correctly configured, from a machine with no mount. It runs the same probe core the desktop app's **Doctor** uses (shared through the open `swarmfile-lib`) and prints the same report (`--json` for scripts).

## Install

```bash
curl -fsSL https://get.swarmfile.com/doctor | sh
```

## Use

```bash
# reachability + the server's descriptor, unauthenticated
swarmfile-doctor --hub-url https://hub.swarmfile.com

# add the block-path probe (needs a key and a project)
SWARMFILE_API_KEY=sf_key_… swarmfile-doctor \
  --hub-url https://your-server --org-id my-org --project-id <id>

# machine-readable, and to a file
swarmfile-doctor --hub-url https://your-server --json --report /tmp/doctor.json
```

`--hub-url` defaults from `$SWARMFILE_HUB_URL`; `--token` from `$SWARMFILE_API_KEY`.

![swarmfile-doctor reads a hub end to end - DNS, TLS, health, the key, the block path, the declared profile - then walks three breakages deployments actually ship: a revoked key, a service that isn't running, and a typo'd project id. Every answer is a real request against a real misconfiguration.](https://swarmfile.com/demo/oss-doctor-debug.cast)

## The checks

| Check | What it proves |
|---|---|
| `dns <host>` | the hub hostname resolves |
| `hub https /health` | `GET /health` (or `/healthz`) answers 2xx over TLS |
| `hub deep health` | `GET /healthz/deep` (a 404 is expected for a community server) |
| `cloud path` | the block route answers 404 for a never-written content id (the storage path is open) |
| `oidc token` | an authenticated project metadata read against the active project (a valid session or token) |
| `server_profile` | the profile descriptor at `GET /api/profile` (`core` on a community server, `hosted` on the hosted service) |

## With the engine running

The same binary completes the full picture when a `swarmfile-engine` is running (the desktop app, or a seed node). It finds the engine through its cache-dir `control.json` and asks it over the local control socket for the checks that need it (peer-to-peer connectivity and local-network peer discovery, the mount, transfer stats, the host firewall, the engine socket) **against whatever hub the engine is configured for**, the hosted cloud or a community server. Those checks come back real; with no engine running they appear with the same names as `pass` + "skipped (requires the engine)". A `--hub-url` still runs the engine-free probes against that hub and merges in the engine's engine-only checks, so you can check one hub while still getting local engine/network state.

## Options and exit codes

Flags mirror the desktop doctor (`--json`, `--quiet`, `--report <path>`, `--hub-url`, `--no-iroh`, `--no-mdns`, `--iroh-peer`, `--cache-dir`, `--latency`, `--verbose`), plus the headless inputs `--token`, `--org-id`, `--project-id` and `--timeout` (seconds). `--latency` adds the per-route timing probe above (three samples each, 2 s per sample, 6 s overall); the desktop tray's **Run doctor** sends the same flag. No report file is written unless you pass `--report <path>`.

`--repair` (with `--yes` to skip the prompt) reinstalls the Swarmfile desktop app from the update channel it was installed from, after verifying the download; on a machine without the desktop app it refuses and points you at the downloads page. See [`swarmfile-doctor --repair`](https://swarmfile.com/docs/cli/swarmfile-doctor#--repair).

Exit codes match the desktop doctor: **0** every check pass, **1** at least one failure, **2** warnings only. With `--repair`: **0** reinstalled, **1** a step failed, **3** refused or canceled with nothing changed. A connection failure or a refused key prints a hint naming what to look at.
