Commit Graph
9 Commits
Author SHA1 Message Date
naps62-yoloandClaude Opus 4.8 8b4cdfd650 fix(maestro): tolerate unknown WS frame types instead of dropping the connection (NAP-269)
ci / rust (push) Successful in 14m9s
`dispatch_text` (registry) and `handle_inbound` (daemon) treated any
frame that didn't deserialize into a known `DaemonOutbound`/`DaemonInbound`
variant as fatal: the registry returned `false` (breaking the handler
loop and deregistering the daemon, wiping its catalog), and the daemon
propagated the serde error out of the read loop. A newer peer emitting a
not-yet-merged variant would therefore get stuck in a tight
disconnect/reconnect loop.

- Add a `#[serde(other)] Unknown` catch-all to both enums so unknown
  `type` tags decode instead of erroring.
- Log-and-skip the `Unknown` variant on both sides.
- Also log-and-skip genuinely malformed frames rather than tearing down
  the connection, so one bad frame can't wipe a daemon's catalog.

Tested: unknown/malformed frames keep the connection alive (registry
handler), and unknown `type` decodes to `Unknown` (protocol round-trip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 19:30:36 +00:00
naps62-yoloandClaude Opus 4.7 c4c9c8a24f feat(maestro): DaemonInbound::CatalogEvent + RegistryEvent::for_peer (NAP-246)
New control-link message wrapping a registry event for daemons that want a
local catalog mirror. `RegistryEvent::for_peer(self_id)` strips events that
concern the recipient itself — daemons mirror peer state, not self.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 19:18:08 +00:00
naps62-yoloandClaude Opus 4.7 45795a144a feat(maestro): add PairAttachClaims JWT shape (NAP-242)
ci / rust (push) Failing after 10m43s
iss/sub/aud/sid/exp claims for the broker-mediated attach flow. "Pair-scoped"
means the credential identifies which home daemon (sub) may attach to which
target daemon (aud) for which session (sid) — a stolen token only works
between the two daemons it was issued for.

Existing JwtClaims stays as a compat path; NAP-243 wires the new claims into
the daemon's verifier and the registry's issuer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:46:43 +00:00
naps62-yoloandClaude Opus 4.7 9a8109559b feat(maestro): extend Heartbeat with perimeter_id + private_addr (NAP-242)
Convert DaemonOutbound::Heartbeat from unit to struct variant with two
optional fields advertising the daemon's T2 reachability. Old payloads
({"type":"Heartbeat"} with no fields) still parse — both fields default
to None — and a new daemon that doesn't populate them serializes back to
the same minimal shape.

Update the daemon's heartbeat ticker and the registry's match arm to the
new shape. Population from daemon config + registry persistence lands
with the broker logic in NAP-243.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:46:05 +00:00
naps62-yoloandClaude Opus 4.7 f0510a5996 feat(maestro): add ResolveAttach + RelaySetup/RelayAck wire variants (NAP-242)
DaemonOutbound: ResolveAttach (daemon → registry request) and RelayAck
(target → registry decision). DaemonInbound: ResolveAttachResult wrapping
RpcResult<AttachCandidates>, and RelaySetup (registry → target signal).
All correlated via reply_to, matching the existing CreateSession pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:43:55 +00:00
naps62-yoloandClaude Opus 4.7 de70a52e5b feat(maestro): add AttachCandidates protocol types (NAP-242)
Add the wire types for broker-mediated attach: PerimeterCandidate (T2 hint),
RelayCandidate (T3 marker, shape TBD via NAP-238), and AttachCandidates as
the registry's reply to ResolveAttach.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:43:10 +00:00
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 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-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