shares add wrote the config file and stopped there, so with a daemon running
the folder stayed invisible to the network until the daemon restarted — the
command reported success and nothing had happened. Locally that is the right
meaning, because there is no session yet; a daemon is already the next run.
Both commands now push the new list over shares.set. The e2e test was checked
against a disabled push to confirm it detects the bug.
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.
The OpenRPC document is the machine-readable contract; daemon-protocol.md
covers what a schema cannot say — framing, the auth handshake, and that
events are pushed, unreplayable, and will disconnect a client that stops
reading.
The daemon drains the library's destructive take_* accessors once and fans
events out per connection, which is what lets more than one client share a
session. A Unix socket at 0600 is the local transport and its permissions
are the authentication; TCP is opt-in and needs the generated token. Rooms,
transfers, and the conversation are daemon-owned and restored on restart.
Methods and events are enums built by one macro, so every consumer matches
exhaustively: adding a member is a compile error until the dispatcher, the
published schema, and the remote client all handle it. docs/openrpc.json is
derived from the DTOs rather than hand-written, and a test fails the build
when the checked-in copy drifts.