Commit Graph
3 Commits
Author SHA1 Message Date
Michel de Graaf 01152d91f6 feat(daemon): say what to do next on startup, and prove multiple clients agree
Starting the daemon printed 'ready' and nothing else, so the token and the
next step were both somewhere else. It now prints what other commands can do
from here, and when --bind is given, the token itself with the two config
commands a remote client needs. All on stderr, so stdout stays empty and
daemon still composes in a pipeline.

Adds the guarantee daemon mode is for as a test: eight clients connected at
once are each answered correctly, none has its replies crossed with another's,
and the daemon still counts all eight. Plus 250 connect-and-drop cycles, well
past the connection cap, to catch a per-connection resource that is not
released — the shape a daemon that works for a while and then refuses
everybody would take.

A connection the daemon drops before it can be served now logs why at warning
level. A client only ever sees 'the connection closed', so that line is the
only account of the reason.
2026-07-27 14:39:31 +02:00
Michel de Graaf f985850c06 test(daemon): route the suite at its chokepoint and cover the protocol directly
Only 25 of the 83 CLI e2e tests actually reached a daemon: the helpers that
build a command bypassed the one place that routed. Routing at
TestServer::args instead brings that to 39 and, more importantly, puts the
streaming tests through a daemon — event.room and event.message delivery to a
remote client's mirror had no end-to-end coverage at all.

Adds what the command surface structurally cannot reach:

- Two clients against one daemon at once, which is the reason the hub exists
  and was proven nowhere in CI.
- tests/daemon_protocol_e2e.rs, speaking the socket by hand: TCP token
  required and socket token not, pre-auth refusal, error ids, notification
  silence, the line cap, the handshake timeout, and that a call shaped by the
  published contract is accepted.
- daemon status/stop/token as commands.
- A TUI unit test for conversation seeding, the part of the attach path that
  had actually been broken.

The protocol suite caught a regression in yesterday's hardening on its first
run: the socket was shut down before the writer flushed a failed handshake's
refusal, so a client with a wrong token saw the connection vanish instead of
being told why.
2026-07-27 12:30:41 +02:00
Michel de Graaf 41307d0510 fix(daemon): correct the published contract and harden the control socket
A review pass over the diff found seventeen confirmed defects. The worst was
the contract itself: every method's parameters were published as a single
descriptor named `params`, so a client generated from openrpc.json sent
{"params":{"params":{…}}} and every call failed -32602. Parameters are now
published per field, with a test asserting the published names are the keys
the daemon actually deserializes.

Security and lifecycle:
- download.start no longer lets a caller name a directory, which was an
  arbitrary write on the daemon's host.
- The Unix socket is bound under a temporary name and renamed into place; it
  was briefly connectable by any local user between bind and chmod, and on
  that transport connecting is authenticating.
- Pre-auth reads are capped at 1 MiB with a 30s handshake timeout, and
  connections are capped, so an unauthenticated peer cannot exhaust memory or
  threads.
- A read error no longer returns past the hub unsubscribe, which leaked a
  subscriber and two threads per occurrence.
- A client whose event queue fills is now actually disconnected. It used to be
  dropped from the fan-out and left connected, working on but blind to every
  event after — the documentation already promised otherwise.
- A client whose daemon vanishes mid-transfer is told at once instead of
  waiting out its full timeout and reporting a misleading "gave up".

Contract accuracy: errors carry an explicit null id (a missing one reads as a
notification and gets dropped), a second auth answers with the published
shape, and nullable fields are sent rather than omitted so a generated client
sees one shape.

Also splits proto.rs and server.rs, whose concerns had diverged, and applies
the review's simplifications.
2026-07-27 11:38:46 +02:00