From f061005c55718dbd7c4b7522fcece940d3a6225d Mon Sep 17 00:00:00 2001 From: naps62-yolo Date: Tue, 26 May 2026 09:36:08 +0000 Subject: [PATCH] docs(maestro): unified-config rollover in installer + INSTALL + spec (NAP-233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Installer: check for a `[daemon]` section in the unified file (rather than the existence of the now-defunct `~/.config/maestrod/config.toml`) to decide whether to skip the configure prompt on re-runs. The configure prompt + maestrod_config_path() helper now reference `~/.config/maestro/config.toml`. - docs/INSTALL.md: mention the unified file and the single-TOML shape. - spec: update §Registry / §Daemon / §Auth to call out `[registry].bind_addr`, `[daemon].bind_addr`, `[shared].registry_url`, and `[shared].token` instead of the old per-app files. --- crates/maestro-installer/src/main.rs | 34 +++++++++++++------ docs/INSTALL.md | 10 +++--- .../specs/2026-05-24-maestro-design.md | 6 ++-- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/crates/maestro-installer/src/main.rs b/crates/maestro-installer/src/main.rs index 2cf12f1..b966b88 100644 --- a/crates/maestro-installer/src/main.rs +++ b/crates/maestro-installer/src/main.rs @@ -96,14 +96,15 @@ fn run(args: Args) -> Result<(), Box> { // 2. Offer to run `maestrod configure` now (must happen before systemd // start, since the daemon refuses to run without registry_url / token). - // Re-runs of the wizard auto-skip when the config already exists; that's - // what makes "re-run to update" pleasant. In non-interactive mode (--yes / - // no TTY) we also skip — `maestrod configure` would prompt for required - // fields and we don't want to surprise scripts. - let configured = if let Some(path) = maestrod_config_path() { - if path.exists() { + // Re-runs of the wizard auto-skip when the unified config already has a + // [daemon] section; that's what makes "re-run to update" pleasant. In + // non-interactive mode (--yes / no TTY) we also skip — `maestrod + // configure` would prompt for required fields and we don't want to + // surprise scripts. + let configured = if let Some(path) = maestro_config_path() { + if daemon_section_present(&path) { eprintln!( - "\nconfig already exists at {} — skipping configure step.", + "\n[daemon] section already present in {} — skipping configure step.", path.display() ); eprintln!("(run `maestrod configure --force` to rewrite it.)"); @@ -175,15 +176,28 @@ fn install_crate( Ok(()) } -fn maestrod_config_path() -> Option { - Some(dirs::config_dir()?.join("maestrod").join("config.toml")) +fn maestro_config_path() -> Option { + Some(dirs::config_dir()?.join("maestro").join("config.toml")) +} + +/// True iff the unified file exists and already has a `[daemon]` section. +/// Used to skip the configure prompt on re-runs without forcing the user +/// to manually delete a co-located `[registry]` / `[cli]` section first. +fn daemon_section_present(path: &Path) -> bool { + if !path.exists() { + return false; + } + let Ok(body) = std::fs::read_to_string(path) else { + return false; + }; + body.lines().any(|l| l.trim_start().starts_with("[daemon]")) } fn run_configure_step(args: &Args, interactive: bool) -> Result> { let want = interactive && !args.no_configure && confirm( - "Run `maestrod configure` now (writes ~/.config/maestrod/config.toml)?", + "Run `maestrod configure` now (writes ~/.config/maestro/config.toml)?", true, )?; if want { diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 267dcaf..cc5482c 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -17,9 +17,10 @@ wizard, and cleans up — only `maestrod` and `maestro` end up persisted in `~/.cargo/bin`. **Updating is the same command.** Re-running `./install.sh` detects an -existing `~/.config/maestrod/config.toml` and existing systemd unit, skips the -configure prompt, refreshes the unit, and runs `systemctl --user restart -maestrod` instead of `start`. No `--update` flag needed. +existing `[daemon]` section in `~/.config/maestro/config.toml` and an +existing systemd unit, skips the configure prompt, refreshes the unit, and +runs `systemctl --user restart maestrod` instead of `start`. No `--update` +flag needed. Inside the script, `cargo install --git https://git.naps.pt/naps62/maestro …` uses your already-configured git credentials for this host (HTTPS helper or @@ -39,7 +40,8 @@ The wizard: 1. Runs `cargo install` for `maestro-daemon` and `maestro-cli` (pinned to the committed `Cargo.lock` via `--locked`). -2. Optionally runs `maestrod configure` to drop a `~/.config/maestrod/config.toml`. +2. Optionally runs `maestrod configure` to populate `~/.config/maestro/config.toml` + (one shared TOML for `maestrod`, `maestro-registry`, and the `maestro` CLI). 3. On systemd hosts, optionally writes a user unit at `~/.config/systemd/user/maestrod.service`, reloads systemd, and offers to enable + start it. diff --git a/docs/superpowers/specs/2026-05-24-maestro-design.md b/docs/superpowers/specs/2026-05-24-maestro-design.md index 9569b69..0a65416 100644 --- a/docs/superpowers/specs/2026-05-24-maestro-design.md +++ b/docs/superpowers/specs/2026-05-24-maestro-design.md @@ -83,7 +83,7 @@ Non-responsibilities: - No persistent storage in the prototype. State is in-memory; daemons re-register on reconnect. - No multi-user model. One shared secret, one effective user. -Runs as a single Rust binary on a designated VM. Listens on a configured `bind_addr` (default `127.0.0.1:8080`; operator sets the actual reachable address — e.g. a Wireguard interface IP — in `~/.config/maestro/registry.toml`). +Runs as a single Rust binary on a designated VM. Listens on a configured `bind_addr` (default `127.0.0.1:8080`; operator sets the actual reachable address — e.g. a Wireguard interface IP — in `[registry].bind_addr` of the unified config at `~/.config/maestro/config.toml`). ### Daemon (`maestrod`) @@ -101,7 +101,7 @@ Non-responsibilities: - Doesn't relay to other daemons. It only knows about local tmux. - Doesn't manage its own auth tokens; the registry issues them. -Runs as a single Rust binary on every host that holds sessions. Small footprint matters because some hosts are LXC containers. Listens on a configured `bind_addr` (default `127.0.0.1:8081`; operator sets the actual reachable address in `~/.config/maestrod/config.toml`). Also requires `registry_url` so it knows where to register on startup. +Runs as a single Rust binary on every host that holds sessions. Small footprint matters because some hosts are LXC containers. Listens on a configured `bind_addr` (default `127.0.0.1:8081`; operator sets the actual reachable address in `[daemon].bind_addr` of the unified config at `~/.config/maestro/config.toml`). Also requires `[shared].registry_url` so it knows where to register on startup. ### Web client @@ -244,7 +244,7 @@ struct Session { ## Auth -- Registry generates `MAESTRO_TOKEN` at first run, printed once, stored in `~/.config/maestro/registry.toml`. +- Registry generates the shared token at first run, printed once, stored as `[shared].token` in `~/.config/maestro/config.toml` (env var name: `MAESTRO_SHARED_TOKEN`). - Same token deployed to each daemon (config file or env var) and used in `Hello`. - Web client gets a cookie from the registry on first visit. All registry API calls use it. - Per-attach JWT issued by registry, signed with the shared secret, validated locally by the daemon. ~5 min expiry; client refreshes by re-requesting `GET /api/sessions/:id/connect-info`.