Skip to content

feat(diagnose): offer a report the user sends themselves - #462

Draft
volen-silo wants to merge 1 commit into
feat/doctor-report-approved-architecturesfrom
feat/doctor-report-sent-by-the-user
Draft

volen-silo wants to merge 1 commit into
feat/doctor-report-approved-architecturesfrom
feat/doctor-report-sent-by-the-user

Conversation

@volen-silo

@volen-silo volen-silo commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #452. Review that one first; this diff only makes sense on top of it, and the base changes to main once it lands.

Summary

  • Builds the prefilled mail that carries a Doctor report, and decides whether a mail client may be started for it. Nothing here sends anything, and there is deliberately nothing here that could: no SMTP client, no credential, nothing to authenticate with.
  • Adds --send, which requires --report and offers that mail.
  • Moves the browser opener behind a trait in rocm-core, out of the ComfyUI path that held the only implementation. There is now exactly one opener in the tree.
  • rocm diagnose --report now ends with the real delivery decision instead of the hardcoded "Sending is not implemented yet".

Why. A report reaches AMD because a person read it and sent it. That rules out anything automatic, and it rules out the CLI holding a credential of any kind.

Risk: low. The ComfyUI move is logic transplanted verbatim and its tests pass unchanged. The rest is new surface behind a new flag.

Destination

Reports go to a mailbox, fixed in code. An address a caller can choose is an address an attacker can choose, and the user would be reading a report they believe goes to AMD while it goes somewhere else. The address is pinned by a test as a literal, because a typo there sends every report somewhere nobody is watching and nothing else would notice.

An earlier revision of this branch targeted a private issue tracker. That was changed deliberately. The report content is unaffected either way; only the transport differs.

What a mailbox costs, and what this does about it

No labels. The classification an issue would carry in metadata moves to the subject line, which is the only place a mail rule and a person scanning an inbox can both read:

[rocm-doctor] fix-8-wheel-rocm on gfx942 / ubuntu-22

Ordered most stable first, so a sorted inbox groups by cause and then by machine.

A subject is as public as a body, and travels further, since it shows in an inbox list. So a field that is not approved for the body is not approved for the subject. A test plants markers for the user name, the ROCm path, the CPU model, the GPU marketing name and the PCI address, and asserts none of them reaches the subject.

Mail clients are often absent on servers, lab machines and containers, which is a large part of the target population. There, a mailto: link does nothing. The printed form therefore names the address on its own, not only inside the link, so the person can send the mail by hand.

A mail identifies its sender. The report carries no user name, no host name and no machine identifier, deliberately. A mail envelope carries the sender's address whatever the body says. The transport identifies a person the payload was designed not to. This is stated in the README where a user decides whether to send, rather than left to be discovered.

Non-obvious decisions

--send requires --report. That makes "the content is shown first" structural rather than a promise: the flag that sends a report cannot be given without the flag that prints it. It refuses --json, because that combination is for scripts, and a script is not a person who can read a mail before sending it.

Two conditions gate a mail client, checked separately. The user has to have asked, and the machine has to look like a desktop they are at. A display is permission from the environment, never from the person, so adding one to a machine must not change what --report alone does. Every unknown answers no, SSH answers no whatever DISPLAY says, and an empty variable is not evidence.

The link body is compared as JSON, not as a Report. Deserializing into the struct discards fields it does not know, so a body carrying a hostname beside the approved fields round-trips to an identical Report and passes. A planted leak survived the first version of that test. This seam is where an extra useful-looking detail would get added, so the check has to be exact.

The address is not percent-escaped; the query values are. RFC 6068 allows @ and . unescaped in the address part, and an escaped @ there is handled poorly by some clients. That is safe because the address is a compile-time constant this crate owns, never text read from a machine.

Deciding and acting are separate functions. The decision is tested in rocm-core across many environments; acting on it is tested against an opener that does not exist, on a machine with no mail client. A function that did both could be verified on neither.

Not in this PR

  • The offline case that writes the report to a file.
  • The dashboard, which is a different crate and a different surface.
  • Anything that counts or groups what arrives in the mailbox. A mailbox has no query, so that needs either a person reading mail or a service holding mailbox credentials. It is an open question, not an oversight.

Test plan

cargo test --workspace --all-targets: 3187 pass, 0 fail. cargo clippy --workspace --all-targets -- -D warnings and cargo fmt --all --check clean, gated on exit status.

Every new guard was checked by mutation, so the tests are known to fail when the thing they describe breaks:

Guard Mutation applied
body is exactly the approved report smuggle a hostname field into it
the subject carries no unapproved field leak the GPU marketing name into it
reports address the agreed mailbox send to a different address
the subject names the matched cause always report unrecognised
a non-open decision never starts a client open on Show
an open decision does start one never open
a failed client still hands over the address drop the link from the message
SSH is never treated as a desktop remove the SSH check
an empty variable is not evidence treat empty as set
query values are escaped stop encoding
asking is required, not just a desktop ignore whether the user asked
a desktop is required, not just asking treat asking as sufficient

Two of those rows exist because the first version of the test did not catch them.

The Linux-only assertions carry #[cfg(target_os = "linux")], because may_open_browser treats Windows and macOS as a desktop outright and there is no environment there that means "not a desktop". An earlier revision asserted the Linux rules unconditionally and failed the Windows lane after passing locally. Verified by simulating that lane: forcing the platform gate and compiling out the Linux guards, the remaining tests pass.

Not verified, and cannot be here: that a real mail client opens, and that mail reaching the mailbox is readable after a client has wrapped and quoted it.

@volen-silo
volen-silo force-pushed the feat/doctor-report-approved-architectures branch from d9ac489 to db0d18e Compare September 30, 2026 10:17
@volen-silo
volen-silo force-pushed the feat/doctor-report-sent-by-the-user branch 3 times, most recently from e49f924 to 1573c97 Compare September 30, 2026 11:32
A report reaches AMD because a person read it and sent it. This builds the
prefilled mail, decides whether a mail client may be started, and offers it
behind `--send`. It holds nothing that could send anything: no SMTP client,
no credential, nothing to authenticate with. That is the design, not an
omission, so the absence is worth keeping visible.

The destination is a mailbox fixed in code. An address a caller can choose
is an address an attacker can choose, and the user would be reading a report
they believe goes to AMD while it goes somewhere else. The address is pinned
by a test as a literal, because a typo there sends every report somewhere
nobody is watching and nothing else would notice.

A mailbox has no labels, so the classification an issue would carry in
metadata moves to the subject line, which is the only place a mail rule and
a person scanning an inbox can both read. It is ordered most stable first,
so a sorted inbox groups by cause and then by machine. A subject is as
public as the body and travels further, since it shows in an inbox list, so
a field not approved for the body is not approved for the subject either,
and a test plants markers to prove none reaches it.

`--send` requires `--report`, which makes "the content is shown first"
structural rather than a promise: the flag that sends a report cannot be
given without the flag that prints it. It refuses `--json`, because that
combination is for scripts and a script is not a person who can read a mail
before sending it.

Two conditions gate a mail client, and both are checked. The user has to
have asked, and the machine has to look like a desktop they are at. Every
unknown answers no, a session reached over SSH answers no whatever DISPLAY
says, and an empty variable is not evidence. The printed form names the
address on its own rather than only inside the link, because a server or a
container usually has no mail client and the person has to send it by hand.

The link body is compared against the report as JSON rather than as a
`Report`. Deserializing discards fields the struct does not know, so a body
carrying a hostname beside the approved fields round-trips to an identical
`Report` and passes. A planted leak survived the first version of that test.

The opener moves behind a trait and out of the ComfyUI path, which held the
only implementation. Callers take the trait, so a test can observe that a
client was deliberately not started, which is otherwise indistinguishable
from one that failed. There is now exactly one opener in the tree.

One cost is worth stating where it is incurred rather than in a footnote: a
mail carries the sender's address, which the report itself deliberately does
not. The transport identifies a person the payload was designed not to.

Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>
@volen-silo
volen-silo force-pushed the feat/doctor-report-sent-by-the-user branch from 1573c97 to eb32981 Compare October 1, 2026 12:16
@volen-silo volen-silo changed the title feat(diagnose): offer a report the user files themselves feat(diagnose): offer a report the user sends themselves Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant