docs(maestro): unified-config rollover in installer + INSTALL + spec (NAP-233)
- 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.
This commit is contained in:
@@ -96,14 +96,15 @@ fn run(args: Args) -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
// 2. Offer to run `maestrod configure` now (must happen before systemd
|
// 2. Offer to run `maestrod configure` now (must happen before systemd
|
||||||
// start, since the daemon refuses to run without registry_url / token).
|
// 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
|
// Re-runs of the wizard auto-skip when the unified config already has a
|
||||||
// what makes "re-run to update" pleasant. In non-interactive mode (--yes /
|
// [daemon] section; that's what makes "re-run to update" pleasant. In
|
||||||
// no TTY) we also skip — `maestrod configure` would prompt for required
|
// non-interactive mode (--yes / no TTY) we also skip — `maestrod
|
||||||
// fields and we don't want to surprise scripts.
|
// configure` would prompt for required fields and we don't want to
|
||||||
let configured = if let Some(path) = maestrod_config_path() {
|
// surprise scripts.
|
||||||
if path.exists() {
|
let configured = if let Some(path) = maestro_config_path() {
|
||||||
|
if daemon_section_present(&path) {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"\nconfig already exists at {} — skipping configure step.",
|
"\n[daemon] section already present in {} — skipping configure step.",
|
||||||
path.display()
|
path.display()
|
||||||
);
|
);
|
||||||
eprintln!("(run `maestrod configure --force` to rewrite it.)");
|
eprintln!("(run `maestrod configure --force` to rewrite it.)");
|
||||||
@@ -175,15 +176,28 @@ fn install_crate(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maestrod_config_path() -> Option<PathBuf> {
|
fn maestro_config_path() -> Option<PathBuf> {
|
||||||
Some(dirs::config_dir()?.join("maestrod").join("config.toml"))
|
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<bool, Box<dyn std::error::Error>> {
|
fn run_configure_step(args: &Args, interactive: bool) -> Result<bool, Box<dyn std::error::Error>> {
|
||||||
let want = interactive
|
let want = interactive
|
||||||
&& !args.no_configure
|
&& !args.no_configure
|
||||||
&& confirm(
|
&& confirm(
|
||||||
"Run `maestrod configure` now (writes ~/.config/maestrod/config.toml)?",
|
"Run `maestrod configure` now (writes ~/.config/maestro/config.toml)?",
|
||||||
true,
|
true,
|
||||||
)?;
|
)?;
|
||||||
if want {
|
if want {
|
||||||
|
|||||||
+6
-4
@@ -17,9 +17,10 @@ wizard, and cleans up — only `maestrod` and `maestro` end up persisted in
|
|||||||
`~/.cargo/bin`.
|
`~/.cargo/bin`.
|
||||||
|
|
||||||
**Updating is the same command.** Re-running `./install.sh` detects an
|
**Updating is the same command.** Re-running `./install.sh` detects an
|
||||||
existing `~/.config/maestrod/config.toml` and existing systemd unit, skips the
|
existing `[daemon]` section in `~/.config/maestro/config.toml` and an
|
||||||
configure prompt, refreshes the unit, and runs `systemctl --user restart
|
existing systemd unit, skips the configure prompt, refreshes the unit, and
|
||||||
maestrod` instead of `start`. No `--update` flag needed.
|
runs `systemctl --user restart maestrod` instead of `start`. No `--update`
|
||||||
|
flag needed.
|
||||||
|
|
||||||
Inside the script, `cargo install --git https://git.naps.pt/naps62/maestro …`
|
Inside the script, `cargo install --git https://git.naps.pt/naps62/maestro …`
|
||||||
uses your already-configured git credentials for this host (HTTPS helper or
|
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
|
1. Runs `cargo install` for `maestro-daemon` and `maestro-cli` (pinned to the
|
||||||
committed `Cargo.lock` via `--locked`).
|
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
|
3. On systemd hosts, optionally writes a user unit at
|
||||||
`~/.config/systemd/user/maestrod.service`, reloads systemd, and offers to
|
`~/.config/systemd/user/maestrod.service`, reloads systemd, and offers to
|
||||||
enable + start it.
|
enable + start it.
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ Non-responsibilities:
|
|||||||
- No persistent storage in the prototype. State is in-memory; daemons re-register on reconnect.
|
- 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.
|
- 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`)
|
### Daemon (`maestrod`)
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ Non-responsibilities:
|
|||||||
- Doesn't relay to other daemons. It only knows about local tmux.
|
- Doesn't relay to other daemons. It only knows about local tmux.
|
||||||
- Doesn't manage its own auth tokens; the registry issues them.
|
- 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
|
### Web client
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ struct Session {
|
|||||||
|
|
||||||
## Auth
|
## 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`.
|
- 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.
|
- 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`.
|
- 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`.
|
||||||
|
|||||||
Reference in New Issue
Block a user