Commit Graph
565 Commits
Author SHA1 Message Date
naps62-yoloandClaude Opus 4.7 18bd385a2a feat(maestro): registry HTTP API, cookie auth, JWT issuance, events WS
ci / rust (push) Successful in 48s
Adds the client-facing surface on the registry (NAP-215). Endpoints:

- GET /api/daemons — list connected daemons (with daemon_url)
- GET /api/sessions — flat session catalog
- POST /api/sessions — forwards CreateSession to chosen daemon, 504 on
  10s daemon timeout
- DELETE /api/sessions/:id — forwards KillSession
- GET /api/sessions/:id/connect-info — returns {daemon_url, jwt} with a
  5min HS256-signed attach JWT
- WS /api/events — snapshot + delta broadcast of catalog and presence
- GET / placeholder served until NAP-218 web bundle lands

Cookie auth middleware mints a long-lived httpOnly JWT cookie on first
request and reissues silently on missing/tampered cookies. Per spec the
perimeter is the operator's network — the cookie is a second factor.

Protocol additions: daemon_url on Hello, DaemonInfo, ConnectInfo,
RegistryEvent. Daemon config + RegistryClient now carry daemon_url.

Registry state now exposes an event broadcast channel (subscribe gives
a snapshot + tokio::sync::broadcast::Receiver) and helper lookups
daemon_for_session / daemon_url.

Tests: 11 reqwest-driven HTTP integration tests cover every endpoint,
the cookie flow, 504 timeout path, worktree forwarding, and events WS
propagation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 17:08:41 +00:00
naps62-yolo 499cffe397 fix(maestro): use AsyncFd for FIFO PTY reader (non-blocking)
ci / rust (push) Successful in 40s
`tokio::fs::File` routes every `.read()` through `spawn_blocking`. On
an idle FIFO opened r+w, that thread blocks in `read()` forever — and
when the test runtime tries to shut down, the blocking-pool thread is
stuck on the syscall and the test binary hangs (which is exactly what
just took out CI run 11).

Switch to `std::fs::File` opened `O_RDWR | O_NONBLOCK` wrapped in
`tokio::io::unix::AsyncFd`. Reads now drive off epoll readiness with
`try_io`; no blocking-pool threads, no hung shutdown.

`Drop` still aborts the task so per-test cleanup is clean.
2026-05-24 16:47:44 +00:00
naps62-yolo 2b84783968 fix(maestro): drop stale wait_for_async import in attach test too
ci / rust (push) Has been cancelled
2026-05-24 16:17:28 +00:00
naps62-yolo fb2a0b1e6a fix(maestro): drop stale wait_for_async import in session_manager test
ci / rust (push) Failing after 1m14s
2026-05-24 16:10:18 +00:00
naps62-yolo 0624ad1257 style(maestro): apply cargo fmt
ci / rust (push) Failing after 1m15s
2026-05-24 15:53:35 +00:00
naps62-yolo f64d9312ee ci(maestro): drop container, install rustup + tmux on default image
ci / rust (push) Failing after 27s
`rust:1.83-bookworm` has no Node, which breaks `actions/checkout@v4`
(it's a JS action). The default gitea runner image has Node, so go back
to that and install rustup + tmux + git + build-essential ourselves.
2026-05-24 15:37:33 +00:00
naps62-yolo 22d079145f ci(maestro): fix runner image + trigger on linear/** branches
ci / rust (push) Failing after 26s
CI has been red since NAP-211 because docker.gitea.com/runner-images:
ubuntu-latest has no `rustup`; the toolchain install step exits 127.

- Use `rust:1.83-bookworm` as the job container so rustup + cargo + a
  stable toolchain are pre-installed
- Install `tmux` + `git` so the daemon integration tests can run
- Add `rustfmt` and `clippy` components
- Also trigger on pushes to `linear/**` so yolo branches see CI before
  they're merged
2026-05-24 15:24:44 +00:00
naps62-yolo ab9a5bf017 feat(maestro): wire daemon to registry + per-session WS stream
Completes the headless daemon stack for NAP-214.

- `config`: daemon TOML config (daemon_id, registry_url, bind_addr,
  token, state_dir, worktree_root) with sensible XDG defaults.
- `jwt`: HS256 verifier; the daemon validates per-attach JWTs locally
  with no callback to the registry. Also exposes `issue()` for tests.
- `pty::PtyBridge`: per-session FIFO + tokio broadcast pump. The FIFO is
  opened r+w so it never sees zero writers — stays alive across
  `pipe-pane` re-issues. Helper `pipe_pane_command()` builds the
  `cat >> <fifo>` shell line.
- `session::SessionManager`: authoritative live-session store. Creates
  worktrees (when requested), spawns tmux, mkfifo + pipe-pane, writes
  sidecar; tears it all down on kill. Emits diff events on a
  broadcast for the registry client. `recover()` adopts surviving
  `maestro-*` tmux sessions on startup and re-pipes them.
- `registry_client::RegistryClient`: persistent outbound WS to the
  registry with exponential backoff. Sends Hello → snapshot, drives a
  select loop that pumps inbound CreateSession/KillSession RPCs into
  the manager (replying with results), emits SessionAdded/Removed/
  Updated diffs from the manager, and heartbeats every 10s.
- `attach`: axum router mounting `WS /sessions/:id/stream?token=<jwt>`.
  Binary frames carry PTY bytes either way (subscribe to broadcast →
  send to client; receive from client → tmux send-keys -l). Text
  frames carry `ClientControl::{Resize,Ping,Pong}`.
- `bin/maestrod`: ties everything together, loads config from
  `MAESTROD_CONFIG` env or `~/.config/maestrod/config.toml`,
  recovers, spawns the registry client, binds the WS server.

Also adds `JwtClaims` to `maestro-protocol` so the daemon and the
future registry JWT issuer (NAP-215) agree on the wire shape.

Integration tests cover session manager round-trip + recovery + PTY
broadcast; attach endpoint JWT (bad/wrong-session/wrong-daemon),
PTY round-trip through real tmux, multi-attach broadcast, ping/pong;
registry client registration + snapshot + diff forwarding +
CreateSession RPC round-trip.

Closes NAP-214.
2026-05-24 15:22:36 +00:00
naps62-yolo c721b5ec62 feat(maestro): add registry daemon-side WS handler and state
ci / rust (push) Failing after 6s
Registry-side primitives for the daemon↔registry conversation. No
client-facing HTTP yet (that's NAP-215).

- `state::Registry`: in-memory store of connected daemons + a session
  catalog keyed by (daemon_id, session_id). Tracks pending CreateSession
  / KillSession RPCs so a single daemon connection can multiplex them.
- `handler::handle_socket`: per-connection loop that authenticates the
  Hello, applies snapshot/diff/heartbeat frames, and resolves matching
  *Result frames. Heartbeat watchdog is a `tokio::time::timeout` around
  the read side — no separate janitor task.
- `server::router_with_timeout`: axum router mounting `WS /daemon`. The
  timeout knob lets tests verify the 30s drop path in milliseconds.
- `token::ensure_token`: first-run bootstrap that generates a 256-bit
  hex token, prints it once to stderr, and persists it to
  `~/.config/maestro/registry.toml`.
- Outbound `create_session` / `kill_session` exposed as the internal
  API NAP-215's HTTP handlers will call.

Integration tests use a real axum server bound to `:0` and drive it via
`tokio-tungstenite`, covering: bad-token reject, snapshot accept, diff
apply (add/update/remove), heartbeat timeout dropping the daemon,
re-registration replacing the catalog, and a full CreateSession RPC
round-trip.

Closes NAP-213.
2026-05-24 13:42:00 +00:00
naps62-yolo 946aa384a0 feat(maestro): add daemon tmux/sidecar/worktree primitives
ci / rust (push) Failing after 3s
Local primitives for `maestrod`, no network code yet:

- `tmux`: subprocess wrapper around new-session, kill-session,
  list-sessions (filtered to maestro-*), send-keys -l, pipe-pane -o,
  resize-window, kill-server.
- `sidecar`: atomic JSON metadata store keyed by session UUID, with
  load_all for daemon restart recovery.
- `worktree`: `git worktree add -b` with collision-proof generated paths
  and `git worktree remove --force` for cleanup_on_kill.

Integration tests use real tmux (isolated via unique socket + temp
TMUX_TMPDIR) and real git (tmpdir repo).

Closes NAP-212.
2026-05-24 13:02:41 +00:00
naps62-yoloandClaude Opus 4.7 777d246aab feat(maestro): add maestro-protocol crate with wire types
ci / rust (push) Failing after 25s
Introduces the maestro-protocol crate (NAP-211): the shared serde
types that the daemon, registry, and clients all serialize across
WebSocket boundaries. Covers:

  - Session, SessionStatus
  - CreateSessionRequest with optional WorktreeSpec
  - RpcResult<T> reply envelope
  - DaemonOutbound (Hello, snapshot, add/remove/update,
    Heartbeat, request results)
  - DaemonInbound (Ack, Error, CreateSession, KillSession)
  - ClientControl text frames (Resize, Ping, Pong, Error)

Schema mirrors the wire-protocol tables in the design spec. Unit
tests round-trip every variant and assert tag/discriminator
stability so future renames break loudly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:35:16 +00:00
naps62-yoloandClaude Opus 4.7 de8db03a18 chore(maestro): scaffold cargo workspace and CI
Adds the empty Cargo workspace (resolver = 2, members = crates/*),
a stable rust-toolchain pin with rustfmt + clippy, and a Gitea
Actions pipeline that runs cargo fmt --check, clippy -D warnings,
and cargo test on every push/PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:35:08 +00:00
naps62-yoloandClaude Opus 4.7 dc963a27f7 docs(maestro): add repo-level CLAUDE.md
Orients future sessions and Linear-driven skills: points at the spec,
the Linear project, the dependency chain (NAP-211 is the starting
point), the target workspace layout, and conventions (TDD, conventional
commits with maestro scope, real tmux+git in integration tests, no
mocking).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:05:27 +00:00
naps62-yoloandClaude Opus 4.7 810a4fbc28 docs(maestro): make network layer agnostic in spec
Removes Tailscale assumptions from the design. Operator configures bind
addresses and is responsible for reachability + perimeter (Wireguard,
private LAN, or any other mechanism); maestro does not police the network
or terminate TLS itself.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:52:17 +00:00
naps62-yoloandClaude Opus 4.7 7af764c1a3 chore(maestro): initial scaffold and brainstorm spec
Adds .claude/linear.json for Linear-driven skills (Maestro project on
team Naps62) and the brainstorm design doc covering architecture, wire
protocol, session model, auth, and prototype scope boundary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:51:03 +00:00