brokerd: courier, carrying pending approvals to gatewayd and answers back
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -6,6 +6,7 @@ reviewer adds findings under "Reviews" once per milestone.
|
||||
|
||||
| Task | Date | Status | Gate runs | First gate | Deviations | Notes | Model |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| M4b/06-brokerd-courier | 2026-09-24 | done | 1 | pass | none | Filled the four `todo!()`s in the copied `courier.rs`: `finished` (`stop.load(SeqCst) || !broker.table().contains(id)`); `pause` (a `while Instant::now() < until && !finished` loop sleeping `timing.tick.min(until.saturating_duration_since(now))`); `frames` (`try_clone` the stream, spawn a reader thread that hands whole frames over an mpsc channel so the courier can `recv_timeout` without losing half a frame, `Ok(rx)`); `answer` (`take` the entry → `Gone {}` if gone, build the `Answer` from `reply.approved`, call `admin::answer`, map the returned `DecisionRecord` through the five-arm match to `Runs`/`Denied`/`Refused`/`Denied`/`Refused`). The four `broker.rs` changes: `RecvTimeoutError` import widened to include `Receiver`, `use crate::courier;` after the `config` use, `let shown = info.clone();` before the insert, step 4 replaced by `courier::alongside(broker, &shown, || wait_for_verdict(...))` matching `Waited`, and the old step 4 moved to `wait_for_verdict` (with the `Waited` enum) before `pub fn alive`. `cargo build -p brokerd` clean; courier test went 1 pass/9 fail then 10 passed three times running; full brokerd suite green; `cargo fmt --all` then `make gate` prints `gate: ok` on the first run. | ? |
|
||||
| M4b/05-brokerd-config-gateway | 2026-09-24 | done | 1 | pass | none | Copied `docs/plans/M4b/files/crates/brokerd/tests/config_gateway.rs` and the new `support/rig.rs` (whose `Config` gained `gateway: None`) into `crates/brokerd/tests/`. In `config.rs`: added the `Gateway { socket: PathBuf }` struct (`#[serde(deny_unknown_fields, default)]`) before `default_podman`, the `gateway: Option<Gateway>` field (with `#[serde(default)]`) after `runner` in `Config`, and the `gateway_socket()` method before `audit_dir` — `None` when the section is absent, the default `<home>/run/gateway/gateway.sock` when the socket is empty, else the path as given. `cargo fmt --all` then `cargo test -p brokerd` green with `config_gateway` 4 passed; `make gate` prints `gate: ok` on the first run. | ? |
|
||||
| M4b/04-brokerd-answer-post | 2026-09-24 | done | 1 | pass | none | Moved `enum Answer` and `struct Answered` from `ledger.rs` into `approvals.rs` before `pub struct Entry` (with doc comments), added `pub use crate::approvals::{Answer, Answered};` next to the `Verdict` use and `DecisionRecord` to the `use proto` line in `approvals.rs`. Added the `post` field to `Answer::Approved`/`Answer::Refused`, threaded it through `ledger::answer` (step 3 binds `post`, step 6 writes it into `AuditEvent::Approval` instead of `None`), and gave the two `bxctl` answers in `admin.rs` `post: None`. Added `Table::contains` before `take`. Copied `ledger_post.rs`, `broker_pending.rs`, `broker_sequence.rs` and `ledger_answer.rs` into `crates/brokerd/tests/`; every suite passes, `ledger_post` 3 passed, `ledger.rs` is 487 lines, and `make gate` prints `gate: ok` on the first run. | ? |
|
||||
| M4b/03-bxctl-deny | 2026-09-24 | done | 1 | pass | none | Copied `docs/plans/M4b/files/crates/bxctl/tests/cli_deny.rs` into `crates/bxctl/tests/`. In `parse`, `"refuse" => parse_refuse(rest, home)` became `"refuse" | "deny" => parse_refuse(rest, home)`, so `deny` produces the same `Command::Refuse` as `refuse` for every argument list including the error cases. Added a blank line and the two-line `bxctl deny <id> [--reason TEXT] [--admin-socket PATH]` / `Another name for refuse.` block to `USAGE` after the `refuse` block, keeping the two-space and six-space indents. The copied test's `deny_parses_as_refuse` and `usage_names_deny` both failed before the changes and pass after; `cargo test -p bxctl` is green and `make gate` prints `gate: ok` on the first run. | ? |
|
||||
|
||||
@@ -558,3 +558,37 @@ again would run it twice.
|
||||
Then send the message again in the thread.
|
||||
|
||||
**Check.** A direct message to Boxmaker is answered.
|
||||
|
||||
## gateway-unreachable
|
||||
|
||||
**What you see.** `brokerd` prints `brokerd: approval <id>: gatewayd is not reachable at <socket>:
|
||||
<reason>; bxctl can still answer it`, then this entry, once per approval. No approval post
|
||||
appears in Mattermost. `bxctl approvals` lists the approval as usual.
|
||||
|
||||
**Why.** `brokerd.toml` has a `[gateway]` section, so each pending approval is carried to
|
||||
`gatewayd` to be posted in Mattermost. The courier could not connect to `gateway.sock`, or
|
||||
`gatewayd` answered that it could not post (Mattermost unreachable). The approval is not lost: it
|
||||
stays pending, `bxctl` can answer it, and the courier tries again every 5 seconds until it is
|
||||
posted or answered. When a later try works, `brokerd` prints `brokerd: approval <id> posted in
|
||||
Mattermost`.
|
||||
|
||||
**Confirm.**
|
||||
|
||||
```sh
|
||||
ls -l <socket> # gatewayd creates it at start; [approvals] socket in gatewayd.toml
|
||||
pgrep -x gatewayd # is it running?
|
||||
```
|
||||
|
||||
and `gatewayd`'s own output: `cannot reach …` there means Mattermost is the problem (see
|
||||
[mattermost-unreachable](#mattermost-unreachable)).
|
||||
|
||||
**Fix.** Start `gatewayd`, or make `[gateway] socket` in `brokerd.toml` and `[approvals] socket`
|
||||
in `gatewayd.toml` name the same path (both default to `<home>/run/gateway/gateway.sock`). To turn
|
||||
Mattermost approvals off, remove `[gateway]` from `brokerd.toml` and restart `brokerd`.
|
||||
|
||||
**Check.** Trigger an `ask` call: its approval is posted in Mattermost, and `!approve <id>` in
|
||||
its thread answers it.
|
||||
|
||||
Where two Boxmakers share a channel thread, address approval commands to one of them
|
||||
(`@<bot> !approve <id>`): a bare `!approve <id>` is taken by every Boxmaker with that id pending
|
||||
in that thread.
|
||||
|
||||
Reference in New Issue
Block a user