Files
werkregie/doc/prs/2026-10-16-PR#93-matrix-control.md
claude-code ec7c982bf0
werkator build succeeded after 01:20
werkator/deploy build succeeded after 00:31
werkator/mutation build failed after 16:16
werkator/vulnerability-data build succeeded after 00:26
The running relay sends the reaction of matrix react (#93)
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-17 15:34:18 +02:00

125 lines
6.7 KiB
Markdown

# PR#93: The Running Relay Sends the Reaction of matrix react
Version: minor
> **WARNING:** This document describes only the change applied in this PR.
> It may already be outdated once the next PR is merged.
> Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
## Related Links
- [werkregie PR#91](https://git.javagil.de/mi/werkregie/pulls/91), `matrix react`, which takes `sync.lock` itself.
- [werkregie PR#92](https://git.javagil.de/mi/werkregie/pulls/92), the refactoring of `MatrixSource` that this PR needs.
## Problem
`matrix react` takes `sync.lock`, which the running relay holds as long as it runs.
An agent stops the relay for each batch of reactions, for about 50 s per call;
meanwhile the dashboard misses the relay's report and says that the connection to the machine is lost.
## Non-Goals
- No other command through the socket, e.g. a message; only a reaction.
- No socket in the network: a Unix domain socket of the user only.
- No change of what `matrix react` does in the room.
## Specification
### Feature: the process that holds `sync.lock` sends the reaction of `matrix react`
#### Scenario#93.01: the running relay sends the reaction
&hellip; so that the relay keeps running and the dashboard keeps its report.
- **Given** the relay of the machine holds `sync.lock` and serves `control.sock`
- **When** an agent runs `werkregie-dashboard matrix react --room "HSAdmin NG" --contains /pulls/329 ✅️`
- **Then** the relay sends the reaction with its own client, and the command prints the relay's outcome, e.g. `REACTED`
##### Verified by
- [MatrixControlTest "the process that serves the socket sends the reaction and answers with its outcome"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixControlTest.kt)
- [MatrixReactorTest "while another process syncs, the command asks it to send the reaction"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactorTest.kt)
- [MatrixReactionsTest "a printed line gives back its outcome; another line ends the command"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactionsTest.kt)
#### Scenario#93.02: without a running relay the command uses a client of its own
&hellip; so that a reaction works also on a machine without a relay.
- **Given** no process holds `sync.lock`, and a `control.sock` of an ended process may be left
- **When** the agent runs the command
- **Then** nobody answers on the socket, and the command takes `sync.lock` and reacts with a client of its own, as before
##### Verified by
- [MatrixControlTest "without a server nobody answers, also on a socket file that an ended process left"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixControlTest.kt)
- [MatrixControlTest "a server replaces the socket file of an ended process"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixControlTest.kt)
#### Scenario#93.03: a holder of the lock that does not answer
&hellip; so that two clients never use the device's store and keys at once.
- **Given** a process holds `sync.lock` but does not serve `control.sock`, e.g. a relay of an older version
- **When** the agent runs the command
- **Then** nothing is sent, and the command says to restart the relay and ends with exit code 3
##### Verified by
- [MatrixReactorTest "a process that holds the lock but does not answer gets no second client, and the command says how to restart it"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactorTest.kt)
#### Scenario#93.04: a failure in the relay
&hellip; so that the agent sees why no reaction was sent.
- **Given** the relay serves `control.sock`, and the room "HSAdmin NG" is not among its rooms
- **When** the agent runs the command
- **Then** the command prints the relay's message on stderr and ends with exit code 3
##### Verified by
- [MatrixControlTest "a failure of the serving process ends the command with its message"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixControlTest.kt)
- [MatrixControlTest "an answer carries the outcome's line or the failure's message"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixControlTest.kt)
#### Scenario#93.05: the socket takes only a reaction
&hellip; so that the socket opens no other way into the user's Matrix account.
- **Given** the relay serves `control.sock`
- **When** a process of the user sends another command, or a reaction without room, text or key
- **Then** the relay sends nothing and answers with an error
##### Verified by
- [MatrixControlTest "the socket takes only a reaction, with room, text and key"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixControlTest.kt)
- [MatrixControlTest "only the user may use the socket, and it is gone when the server ends"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixControlTest.kt)
## Solution
- `MatrixControl`, new: the Unix domain socket `<stateDir>/matrix/control.sock`, one request per connection,
one line of JSON each way: `{"command":"react","room":…,"contains":…,"key":…}`,
answered by `{"line":"REACTED …"}` or `{"error":"…"}`.
`serve` replaces a socket file that an ended process left, sets it to 0600, and answers one request after the other;
`ask` returns null when nobody serves, so that the command falls back to a client of its own.
- `MatrixSource.sync` serves the socket with its client while it syncs: the relay as well as the local mode.
- `MatrixReactor.react(dir, …)` asks the socket first and takes `sync.lock` only when nobody answers;
`react(client, …)` waits for the first sync and reacts with any client, the relay's or its own.
- A holder of the lock that does not answer, e.g. a relay of an older version or one in its first seconds,
makes the command fail with the hint to restart it; the command never opens a second client.
- `MatrixReactions.outcome` reads the relay's line back into the outcome, so that exit code and output stay as before.
- The socket needs no secret: the directory of the device's keys is 0700, the socket 0600,
so only processes of the user reach it, which could run `matrix react` themselves.
- `pitestPartial mutationCrapCheck` against main: the highest Mutation-CRAP is 13, `MatrixReactor.entry`;
of `MatrixControl` 5, `request`.
## Release Notes
- `matrix react` sends its reaction through the running relay or local mode; neither stops for it any more.
## Open Questions
None.
## Prerequisite PRs
- [werkregie PR#92](https://git.javagil.de/mi/werkregie/pulls/92): each method of `MatrixSource` at a Mutation-CRAP of at most 15;
this branch builds on it.