mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
`marmot.group.avatar-url.v1` (0x8007) is the lightweight alternative to the encrypted Blossom blob: a link, two opaque render hints, no key material. We had neither the codec nor a carrier for it, so a group MDK gave a URL avatar rendered as if it had none. The hard part is not the struct, it is that the URL is canonical state. The spec makes normalization a producer-side encoding rule and requires a decoder to re-run the WHATWG parse-and-serialize and REJECT bytes that differ — never repair them, because two members repairing differently hold different bytes for the same group. So `MarmotHttpsUrl` is a WHATWG serializer, not a validator with a regex: lowercased scheme and host, the default port dropped, dot-segments resolved against a segment list (a trailing slash is a final empty segment, which is also why `/a/.` keeps one), percent-encoding normalized with existing triplets left verbatim. The vectors in the test come from the Rust `url` crate the reference implementation uses, so the two agree byte for byte. Non-ASCII hosts are refused rather than guessed at: IDNA is not implemented here, and a wrong punycode encoding would be worse than a refusal. Contact safety is deliberately a separate function. A URL can be perfectly valid group state and still be somewhere this client refuses to go, and the spec is explicit that the fetch decision "MUST NOT affect component or commit validity" — so the SSRF check lives at the renderer, where an unsafe destination falls back to the Blossom image instead of erroring. Clearing writes the canonical empty state rather than removing the component. Removal is not a free substitute: a component MUST NOT be removed while `app_components` still lists it as required, so a remove is only legal in the same Commit that stops requiring it — and the empty state is what the reference implementation writes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016kCuA6tc4JQzHPCDd39GHq