chore: take h2 0.4.19 for RUSTSEC-2026-0258, and two documentation fixes #4

Merged
jlxq0 merged 2 commits from chore/h2-advisory-and-docs into main 2026-08-26 06:20:15 +00:00
Owner

cargo audit is a CI gate and it has been failing since the advisory published
on 2026-08-17 — the same day this repository's last commit landed, so no CI run
has ever seen it.

Crate h2
Version 0.4.15 → 0.4.19
ID RUSTSEC-2026-0258, "h2 unbounded empty DATA frames"
Solution >=0.4.16

Lockfile only. Cargo.toml is untouched and h2 stays transitive.

Exposure, measured rather than assumed

h2 reaches this tree three ways: reqwest for the outbound Hevy calls, tonic
for OTLP export, and whatever axum::serve negotiates inbound. The inbound path
is the one that would matter, and it is not open. Against the running pod, port
3000, port-forwarded:

$ curl -v --http2-prior-knowledge http://127.0.0.1:18302/health
> GET /health HTTP/2
* Request completely sent off
* Closing connection                 # no response

$ curl -D- http://127.0.0.1:18302/health
HTTP/1.1 200 OK

The process does not answer h2c. The edge terminates HTTP/2 —
curl -w '%{http_version}' https://hevy-mcp.oddie.app/health reports 2 — and
speaks HTTP/1 to us. What remains is a hostile or compromised server we call
outbound, which is narrower than the advisory reads on its own.

The gate is red either way, and a red gate blocks every other change, which is
why this is not deferred on the strength of the exposure being narrow.

Two documentation fixes that need no code

AGENTS.md's session protocol told every agent to read Plan.md first.
There is no Plan.md and there should not be one: anything with a state belongs
in a Forgejo issue, where in a year it still says what was asked, what was
argued, what was decided and which commits did it. A file has no state, so
status written into one is always wrong. The line is replaced with the rule
rather than deleted, so that the next reader does not helpfully create the file.

CLAUDE.md did not exist. It is one line, @AGENTS.md. Both Claude and
Codex read AGENTS.md; two instruction files means two copies and one of them
wrong.

Verification

All five gates on 1.98.0, the version ci.yml pins as of #2, run in the
workflow's own order:

fmt      PASS
clippy   PASS
test     PASS     44 passed, 0 failed
audit    PASS     <- was the only failure before this change
deny     PASS

cargo +1.93.0 check --all-features --locked passes, so the build floor is
unmoved.

`cargo audit` is a CI gate and it has been failing since the advisory published on 2026-08-17 — the same day this repository's last commit landed, so no CI run has ever seen it. | | | |---|---| | Crate | `h2` | | Version | 0.4.15 → **0.4.19** | | ID | RUSTSEC-2026-0258, "h2 unbounded empty DATA frames" | | Solution | `>=0.4.16` | Lockfile only. `Cargo.toml` is untouched and `h2` stays transitive. ## Exposure, measured rather than assumed `h2` reaches this tree three ways: `reqwest` for the outbound Hevy calls, `tonic` for OTLP export, and whatever `axum::serve` negotiates inbound. The inbound path is the one that would matter, and it is not open. Against the running pod, port 3000, port-forwarded: ``` $ curl -v --http2-prior-knowledge http://127.0.0.1:18302/health > GET /health HTTP/2 * Request completely sent off * Closing connection # no response $ curl -D- http://127.0.0.1:18302/health HTTP/1.1 200 OK ``` The process does not answer h2c. The edge terminates HTTP/2 — `curl -w '%{http_version}' https://hevy-mcp.oddie.app/health` reports `2` — and speaks HTTP/1 to us. What remains is a hostile or compromised server we call outbound, which is narrower than the advisory reads on its own. The gate is red either way, and a red gate blocks every other change, which is why this is not deferred on the strength of the exposure being narrow. ## Two documentation fixes that need no code **`AGENTS.md`'s session protocol told every agent to read `Plan.md` first.** There is no `Plan.md` and there should not be one: anything with a state belongs in a Forgejo issue, where in a year it still says what was asked, what was argued, what was decided and which commits did it. A file has no state, so status written into one is always wrong. The line is replaced with the rule rather than deleted, so that the next reader does not helpfully create the file. **`CLAUDE.md` did not exist.** It is one line, `@AGENTS.md`. Both Claude and Codex read `AGENTS.md`; two instruction files means two copies and one of them wrong. ## Verification All five gates on 1.98.0, the version `ci.yml` pins as of #2, run in the workflow's own order: ``` fmt PASS clippy PASS test PASS 44 passed, 0 failed audit PASS <- was the only failure before this change deny PASS ``` `cargo +1.93.0 check --all-features --locked` passes, so the build floor is unmoved.
chore: take h2 0.4.19 for RUSTSEC-2026-0258, and two documentation fixes
Some checks failed
CI / cargo (pull_request) Successful in 2m22s
CI / docker (pull_request) Failing after 2s
21aca10356
`cargo audit` is a CI gate and it has been failing since the advisory
published on 2026-08-17 — the same day this repository's last commit
landed, so no CI run has ever seen it. Lockfile only; `Cargo.toml` is
untouched and `h2` remains transitive.

  Crate:    h2
  Version:  0.4.15 -> 0.4.19
  ID:       RUSTSEC-2026-0258, "h2 unbounded empty DATA frames"
  Solution: >=0.4.16

Exposure, measured rather than assumed. `h2` reaches this tree three
ways: reqwest for the outbound Hevy calls, tonic for OTLP export, and
whatever `axum::serve` negotiates inbound. The inbound path is the one
that would matter and it is not open — an `--http2-prior-knowledge`
request to the running pod's port 3000 gets no response and the
connection closes, while the same path over HTTP/1.1 returns
`HTTP/1.1 200 OK`. The edge terminates HTTP/2 and speaks HTTP/1 to us.
So what is left is a hostile or compromised server we call, which is
narrower than the advisory reads. The gate is red either way.

Two documentation fixes that need no code:

`AGENTS.md`'s session protocol told every agent to read `Plan.md` first.
There is no `Plan.md` and there should not be: anything with a state
belongs in a Forgejo issue, where it still says in a year what was asked
and what was decided. A file has no state, so status written into one is
always wrong. Replaced with the rule rather than deleted, so the next
person does not helpfully create the file.

`CLAUDE.md` did not exist. It is one line, `@AGENTS.md`, because both
Claude and Codex read `AGENTS.md` and two instruction files means two
copies with one of them wrong.

Gates on 1.98.0, the version ci.yml now pins: fmt, clippy, test (44
passed), audit and deny all pass. `cargo +1.93.0 check --all-features
--locked` still passes, so the build floor is where it was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCiHcHN6LrhiuLXbG9SBYG
ci: re-trigger the docker job
All checks were successful
CI / cargo (pull_request) Successful in 44s
CI / docker (pull_request) Successful in 1m2s
a98edf6c4f
Run 16913 died two seconds after starting, which is the shape of a
`Set up job` failure rather than a build failure — no step ran. Pushing
an empty commit to find out whether it is reproducible or transient.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCiHcHN6LrhiuLXbG9SBYG
jlxq0 merged commit ee7c7763ce into main 2026-08-26 06:20:15 +00:00
jlxq0 deleted branch chore/h2-advisory-and-docs 2026-08-26 06:20:16 +00:00
Sign in to join this conversation.
No reviewers
No labels
waiting-on-julian
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jlxq0/hevy-mcp!4
No description provided.