feat(install): pivot happy path to fabro server start web wizard

The primary install flow is now to start the server, complete setup in a
browser-based wizard, and restart. fabro install is retained as the
headless CLI-only alternative.

- Auto-open the install URL in the user's browser when fabro server start
  enters install mode; print a manual-open fallback when open::that fails
- Rewrite install.md so agents drive the full start → wait → restart loop
- Retarget install.sh Y/n prompt from fabro install to fabro server start
- Update README, quick-start, deploy-server, cli reference, and marketing
  captions to point at fabro server start as the next step after download
- Add troubleshooting entries for "wizard didn't open" and "server exited
  after wizard"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-04-21 15:58:00 -04:00
parent 710f9869f0
commit a8599a924f
No known key found for this signature in database
9 changed files with 94 additions and 18 deletions

View file

@ -24,6 +24,8 @@ brew install fabro-sh/tap/fabro-nightly
curl -fsSL https://fabro.sh/install.sh | bash
```
Then run `fabro server start` to finish setup in your browser. The server opens a web wizard, exits when the wizard completes, and starts in configured mode the next time you run it.
<img src="docs/images/runs-board.png" alt="Fabro Runs board showing workflows across Working, Pending, Verify, and Merge stages" />
---
@ -127,15 +129,18 @@ curl -fsSL https://fabro.sh/install.sh | bash
Release binaries and the multi-arch Docker image ship with SLSA Build Provenance attestations. See [Verifying Releases](https://docs.fabro.sh/reference/verifying-releases) to check an artifact was built by our GitHub Actions workflow.
Then initialize Fabro in your project:
Then finish setup in your browser and initialize Fabro in your project:
```bash
fabro install # one-time setup
fabro server start # opens a web install wizard in your browser
# (server exits when the wizard finishes — start it again to run Fabro)
cd my-project
fabro repo init # per project
```
For headless or scripted environments, `fabro install` runs the same setup as a CLI-only wizard.
---
## Self-host the Fabro server

View file

@ -4,11 +4,11 @@
I want you to install Fabro for me. Execute the steps below autonomously.
OBJECTIVE: Install the Fabro CLI binary and verify it works.
OBJECTIVE: Install the Fabro binary, then start the server so the user can finish setup in a web wizard, then restart the server in configured mode.
DONE WHEN: `fabro --version` prints a version string.
DONE WHEN: The Fabro server has been restarted after the wizard and is running in configured (non-install) mode.
## Choose install method
## 1. Install the binary
Check whether Homebrew is available:
@ -28,14 +28,53 @@ Otherwise, use the install script:
curl -fsSL https://fabro.sh/install.sh | bash
```
## Verify
## 2. Verify the binary
```bash
fabro --version
```
This must print a version string like `fabro 0.x.y`. If it does, installation is complete.
This must print a version string like `fabro 0.x.y` before continuing.
Tell the user to run `fabro install` in their terminal to complete setup.
## 3. Start the server to open the install wizard
EXECUTE NOW: Complete the above steps to achieve: `fabro --version` prints a version string.
Start the server in the foreground and capture its output:
```bash
fabro server start
```
The server will:
- Detect that Fabro is unconfigured and enter **install mode**
- Print a URL that looks like `http://127.0.0.1:<port>/install?token=…`
- Attempt to open that URL in the user's default browser
Your job:
- Read the server's stderr and find the `http://…/install?token=…` line
- Open that URL in the user's default browser yourself as a safety net (the server also tries). Use `open` on macOS, `xdg-open` on Linux, or `start` on Windows. Duplicate opens are harmless.
- Tell the user: "The Fabro install wizard is open in your browser. Complete it there; I'll restart the server when you're done."
- Keep the `fabro server start` process running and watch for it to exit. The server exits on its own a moment after the user finishes the wizard.
## 4. Restart the server in configured mode
When the `fabro server start` process exits, start it again:
```bash
fabro server start
```
This time it will boot in configured (non-install) mode.
## 5. Confirm completion
Verify the server is running and no longer in install mode:
```bash
curl -fsS http://127.0.0.1:<port>/health
```
The response should **not** contain `"mode": "install"`. If it does, the wizard wasn't completed — tell the user to finish it and wait for the server to restart again.
EXECUTE NOW: Complete steps 1–5. You are done when the restarted server reports a non-install health status.

View file

@ -158,14 +158,14 @@ else
fi
echo "" >&2
# --- Prompt to run setup wizard ---
# --- Prompt to start the server and open the install wizard ---
if [ -t 2 ] && [ -e /dev/tty ]; then
printf " ${BOLD}Run ${BOLD_CYAN}fabro install${RESET}${BOLD} now to complete setup? [Y/n]${RESET} " >&2
printf " ${BOLD}Run ${BOLD_CYAN}fabro server start${RESET}${BOLD} now to finish setup in your browser? [Y/n]${RESET} " >&2
read -r answer </dev/tty
case "$answer" in
[nN]*) dim "Skipping. Run ${BOLD_CYAN}fabro install${RESET}${DIM} whenever you're ready." ;;
*) echo "" >&2; exec "${INSTALL_DIR}/fabro" install ;;
[nN]*) dim "Skipping. Run ${BOLD_CYAN}fabro server start${RESET}${DIM} whenever you're ready." ;;
*) echo "" >&2; exec "${INSTALL_DIR}/fabro" server start ;;
esac
else
info "Run ${BOLD_CYAN}fabro install${RESET} to complete setup."
info "Run ${BOLD_CYAN}fabro server start${RESET} to finish setup in your browser."
fi

View file

@ -152,6 +152,7 @@ const cssExample = `<span class="text-ice-300">/* All nodes default to fast + ch
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
</button>
</div>
<p class="mt-2 text-center text-xs text-ice-300/60">Then run <code class="font-mono text-teal-300">fabro server start</code> to finish setup in your browser.</p>
</div>
<div class="flex items-center gap-4">
<a
@ -647,6 +648,7 @@ const cssExample = `<span class="text-ice-300">/* All nodes default to fast + ch
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
</button>
</div>
<p class="mt-2 text-center text-xs text-ice-300/60">Then run <code class="font-mono text-teal-300">fabro server start</code> to finish setup in your browser.</p>
</div>
<div class="flex items-center justify-center gap-4">
<a

View file

@ -30,13 +30,22 @@ Both interfaces use the same workflow engine, the same Graphviz files, and the s
fabro server start
```
This starts the server on a Unix socket at `~/.fabro/fabro.sock` by default. Use `--bind 127.0.0.1` for TCP. To also run the web UI:
This starts the server on a Unix socket at `~/.fabro/fabro.sock` by default. Use `--bind 127.0.0.1` for TCP.
### First run: web install wizard
If `~/.fabro/settings.toml` does not yet exist, `fabro server start` enters **install mode**: it prints an install URL, attempts to open the URL in your default browser, and serves a web wizard that walks you through configuring your LLM provider, server URL, and GitHub integration.
When you finish the wizard, the server writes `~/.fabro/settings.toml` and exits cleanly. Start it again to boot in configured mode:
```bash
fabro server start # API on port 3000
cd apps/fabro-web && bun run dev # rebuilds web assets on change; refresh the browser
fabro server start
```
Under a process supervisor with a restart policy (for example docker-compose `restart: unless-stopped`, systemd, or Railway's restart-on-exit) this second start happens automatically.
For headless or scripted environments where no browser is available, run `fabro install` instead — it's the same wizard as a CLI prompt flow.
Common flags:
| Flag | Default | Description |

View file

@ -21,6 +21,10 @@ It checks:
## Common issues
**Install wizard didn't open in my browser** — `fabro server start` prints the install URL to stderr before attempting to open it. Copy that URL into your browser manually. Auto-open fails on headless Linux hosts, inside most containers, and over SSH sessions without a display.
**Server exited after I finished the install wizard** — Expected. The server writes `~/.fabro/settings.toml` and exits cleanly at the end of the wizard. Start it again with `fabro server start` to boot in configured mode, or run it under a supervisor with a restart policy (for example docker-compose `restart: unless-stopped`, systemd, or Railway's restart-on-exit) so the second start happens automatically.
**"No API key configured"** — Set at least one provider key with `fabro provider login` or `fabro secret set`, or export it in the server process environment. Run `fabro doctor` to verify connectivity.
**Stall watchdog timeouts** — If runs are cancelled unexpectedly, the agent may be stuck or the LLM provider may be slow. Check `FABRO_LOG=debug` output for `Agent.LlmRetry` events. Increase `stall_timeout` in the graph if needed, or add [fallback providers](/core-concepts/models) to handle outages.

View file

@ -39,6 +39,10 @@ Fabro has two modes:
Release binaries and the multi-arch Docker image ship with [SLSA Build Provenance](/reference/verifying-releases) attestations you can verify with `gh attestation verify`.
</Tip>
<Note>
Setting up server mode instead? Run `fabro server start` to finish setup in a browser-based wizard. See [Running the Fabro Server](/administration/deploy-server) for the full flow.
</Note>
## Initialize your project
```bash

View file

@ -376,6 +376,10 @@ Demo mode is per-request: send the `X-Fabro-Demo: 1` header to get static demo d
When `--no-web` is set, the server still exposes the machine API under `/api/v1` and `/health`, but it returns `404` for `/`, `/auth/*`, SPA client routes, and the web-only helper endpoints under `/api/v1`.
### Install mode
If `~/.fabro/settings.toml` does not exist yet, `fabro server start` enters install mode: it prints an install URL, attempts to open it in your default browser, and serves a web wizard for configuring LLM providers, the server URL, and GitHub integration. The server exits cleanly when the wizard finishes; start it again (or let a supervisor restart it) to boot in configured mode. See [Running the Fabro Server](/administration/deploy-server) for the full flow, and `fabro install` for the headless CLI-only equivalent.
## `fabro server stop`
Stop the running server daemon. Sends SIGTERM and waits for graceful shutdown, escalating to SIGKILL after the timeout.
@ -855,7 +859,11 @@ For OpenAI, this offers a device code flow — you open a URL in your browser, e
## `fabro install`
Interactive setup wizard that walks you through configuring API keys and validating your environment. Runs `fabro doctor` with live probes after setup to validate the configuration.
Interactive CLI setup wizard that walks you through configuring API keys and validating your environment. Runs `fabro doctor` with live probes after setup to validate the configuration.
<Note>
For most users, the preferred way to set up server mode is to run `fabro server start`, which opens a web-based install wizard in the browser (see [Running the Fabro Server](/administration/deploy-server)). `fabro install` is the headless, CLI-only alternative — useful for remote hosts without a browser, scripted deployments, and CI.
</Note>
```bash
fabro install

View file

@ -226,6 +226,11 @@ fn announce_install_mode(bind: &Bind, token: &str, styles: &Styles, printer: Pri
Some(url) => {
fabro_util::printerr!(printer, " Open this URL in your browser to finish setup:");
fabro_util::printerr!(printer, " {url}");
if let Err(e) = open::that(&url) {
fabro_util::printerr!(printer, "");
fabro_util::printerr!(printer, " Could not open a browser automatically: {e}");
fabro_util::printerr!(printer, " Open the URL above manually to continue.");
}
}
None => {
fabro_util::printerr!(