# M6b task 06: allowlisted POST; audit body equals the bytes **Branch:** `m6b`. Check it: `git branch --show-current` prints `m6b` and `git status --short` is empty; otherwise stop. Never switch branch. **Commit subject:** `brokerd: consult posts the allowlisted body` **Log row:** the Task cell is `M6b/06-brokerd-consult-http`. **First action.** Run this before any Read of crate source. Do not `ls` `crates/brokerd/tests/`. Do not Read `podman.rs`, `docs/implementer-log.md` (except the first 12 lines at the log step). The given test injects an in-process fake; do not open a socket in the tests and do not reach the network (T27). ```sh cp docs/plans/M6b/files/crates/brokerd/tests/consult_allow.rs crates/brokerd/tests/ ``` ## Goal An allowed `consult` POSTs the allowlisted JSON (`wire_body`) through `ConsultTransport`. Tests inject a fake; production uses loopback HTTP/1.1 (`HttpTransport`). The audit `body` equals the posted bytes. `messages` has one user item and no `tools`. The tool result is `choices[0].message.content`, tag-neutralised, then capped. `ConsultStack` sends `Consult` to that runtime and everything else to `inner`, so existing `Routed` tests stay unedited. `main` loads the secret after `Config::load` and wraps `Routed` when `[consult]` and the secret are both present. ## Every place that changes | Place | What happens there | |---|---| | `crates/brokerd/src/consult.rs` | `ConsultTransport`, `ConsultRuntime`, `ConsultStack`, `parse_message_content` | | `crates/brokerd/src/consult_http.rs` | new: `HttpTransport` | | `crates/brokerd/src/lib.rs` | `pub mod consult_http;` | | `crates/brokerd/src/main.rs` | `load_secret`; wrap `Routed` in `ConsultStack` | | `crates/brokerd/tests/consult_allow.rs` | new, given | | everything else | **no change**: not `ledger`, not `handle`'s decide path | ## Files - Copy, from `docs/plans/M6b/files/` (the same path under it): the new test `crates/brokerd/tests/consult_allow.rs`. - Create: `crates/brokerd/src/consult_http.rs` (the module below). - Modify: `crates/brokerd/src/consult.rs`, `crates/brokerd/src/lib.rs`, `crates/brokerd/src/main.rs`, `docs/implementer-log.md`. - Do not touch: any other file. The given test holds `mod runtime` even though it does not name `Recording`: `support/client.rs` imports `crate::runtime`. Leave that module in the test. ## Facts you need - **`Runtime::run_labelled` default calls `run`.** Implement both: `run` forwards to `run_labelled` and drops the label. Implementing only `run_labelled` recurses. - **No `choices[0]` index.** `as_array().and_then(|a| a.first())`. A missing content is `"the response has no content"`. - **Neutralise then cap.** `bxcore::sanitize::neutralise` then `cap_bytes(..., QUESTION_CAP)`. The given test builds the think tag with `concat!("<", "think", ">")` and asserts `<` in the result. Do not write that tag whole anywhere (T33). - **Label.** `run_labelled` returns `(RunOutput { content, truncated }, None)`. Result class comes from the grant, as today. - **HTTP/1.1.** `TcpStream` to the loopback host:port from `parse_loopback_http`. Headers: `POST {path} HTTP/1.1`, `Host`, `Authorization: Bearer {bearer}`, `Content-Type: application/json`, `Content-Length`, `Connection: close`. Response cap 262_144 bytes. Status must contain ` 200 ` or end with ` 200`. HTTPS is later. - **`load_secret` in `main`** after `Config::load`, before the instance lock. Print `{e}` and exit 1 on `Err`. Print a `warning` if `Loaded` has one. Role is already `"brokerd"` inside `load_secret`. - **Wrap after `Routed`.** `ConsultStack { consult: ConsultRuntime::new(consult, loaded.secret, HttpTransport), inner: runtime }` when both `cfg.consult` and the loaded secret are `Some`. Otherwise keep `runtime`. - The items the edits use. This table is complete. | Item | Signature | |---|---| | `Runtime::run` | `run(&self, spec: &RunSpec) -> Result` | | `Runtime::run_labelled` | `run_labelled(&self, spec: &RunSpec) -> Result<(RunOutput, Option