Repository navigation
Description
Problem
SignedArtifact::parsed() (pkgs/id/src/artifact.rs) accepts non-NFC text in the two known kinds that carry free text, while every sibling text path in the same crate refuses non-NFC:
DOCUMENTarm (artifact.rs:356-371): checks onlymedia_typeempty / >128 bytes and content size. A decomposed media type such astext/cafe\u{301}(e + combining acute U+0301) is accepted and stored.TAGarm (artifact.rs:372-385): checks only subject hash, name 1–64 bytes, value ≤256 bytes. A decomposed tag name (aute\u{301}ur) or value (cafe\u{301}) is accepted and stored.
The sibling rules that these kinds bypass:
documents.rsDocumentRef::check()(documents.rs:105-121) refuses a media type that is not NFC — its doc comment states the rule and it is pinned bytest_the_media_type_is_one_to_128_bytes_in_nfc. Same 128-byte cap as the artifact arm, minus the NFC half.tag_claims.rscheck_text()(tag_claims.rs:146-160), used bycheck_opfortag.set, requires NFC for name and value.chain.rs(chain.rs:198, chain.rs:367) requires NFC for event kinds and text strings.
Why it matters
An artifact is a signed, stored, propagated statement: parsed() is the trust-boundary check before a known kind is applied (directory.rs publishes/applies Kind::Document and Kind::Tag). Two spellings of the same media type / tag name — NFC and decomposed — are different byte strings, so lookups, equality, and tag-name matching split on a difference no user can see: a document published with a decomposed media type does not match the NFC spelling a reader asks for, and a decomposed tag name forms a separate tag from the composed one. The event/chain side of the same system canonicalizes on NFC; the artifact side silently does not, so the same logical statement is valid in one store and refused in the other.
Evidence (verified this run)
- Code: artifact.rs:356-385 (above) vs documents.rs:113-117 and tag_claims.rs:152-154 — the NFC check exists in both sibling validators and is absent from both artifact arms.
- Laptop probe (probe-2026-10-31-laneA-nfc, on the laptop clone at the same HEAD 236cb98, artifact.rs sha256 bdc11bbe on both clones) driving the real
SignedArtifact::sign+parsed(): control NFC document/tag ACCEPTED; non-NFC media_type, tag name, and tag value all ACCEPTED byparsed(). - Dedup: open + recently closed search for NFC / non-NFC / normalization against artifact kinds found no existing issue covering the artifact
document/tagarms (sibling of id(artifact): home_declared world_id gets only an is_empty check in parsed() — a 100 KB, whitespace-only, control- or bidi-bearing world_id is accepted and stored, and the same endpoint can be listed twice with different worlds #3187 home_declared world_id and id(artifact): friend_removerequestsis the one known-kind body parsed() never validates — any count, any shape, any length passes, and every string lands permanently in the directory's removed_requests void guard #3146 friend_remove requests — same function, different kinds/fields/rules; the parsed-shape contract design is Design: parsed is not validated — one shape contract at every JSON trust boundary #3068).
Acceptance criteria
parsed()refuses adocumentwhosemedia_typeis not NFC, with aRefusal::Forbiddennaming the rule, pinned by a parsed()-level test using a decomposed spelling.parsed()refuses atagwhosenameorvalueis not NFC, likewise pinned.- Ordinary NFC documents and tags (existing artifact tests) parse exactly as today.
- The check is shared with, or explicitly mirrors, the existing NFC rules in
documents.rs/tag_claims.rsso the three validators cannot drift again (e.g. one helper, or a test that walks all three).
Activity
dezren39 commentedon Oct 31, 2026
🤖 Agent note · filed
SignedArtifact::parsed()(artifact.rs:356-385) accepts non-NFC text in thedocumentandtagarms while documents.rsDocumentRef::check()(:113) and tag_claims.rscheck_text()(:152) both refuse non-NFC for the same fields; verified on main 236cb98 with a laptop probe (probe-2026-10-31-laneA-nfc, real sign+parsed(), artifact.rs sha256 bdc11bbe on both clones): NFC controls ACCEPTED, decomposed media_type / tag name / tag value all ACCEPTED. Sibling of id(artifact): home_declared world_id gets only an is_empty check in parsed() — a 100 KB, whitespace-only, control- or bidi-bearing world_id is accepted and stored, and the same endpoint can be listed twice with different worlds #3187/id(artifact): friend_removerequestsis the one known-kind body parsed() never validates — any count, any shape, any length passes, and every string lands permanently in the directory's removed_requests void guard #3146 (same function, different kinds/rules). Enqueued as a repo-task/v1 packet on repo-health-code-pilot.dezren39 commentedon Oct 31, 2026
🤖 Design-architect pass 28 (2026-10-31) — agent note: this issue is ADOPTED as a phase of design #3231 ("a signed artifact is validated as strictly as it is built", design doc: files/designs/code-id-artifact-receipt-validation.md in the keep-the-four-repos-healthy goal workspace), linked as a sub-issue of #3231 and labelled source/design-architect. Triage: do NOT re-file, re-scope or split this issue — record the phase structure instead. Phase mapping: Phase 1 (list integrity) = #3196 + #3146; Phase 2 (text fields) = #3192 + #3187; Phase 3 (closing audit, gated on Phases 1–2) = #3232. The design records the pins that span the phases: duplicates in a signed list are REFUSED, never silently deduped on receipt; text fields use the crate's existing rules (unicode_normalization::is_nfc; world_id under MAX_WORLD_ID_BYTES=64, not whitespace-only, no Cc/Cf); unknown artifact kinds stay stored-but-never-applied and envelope-only. Each origin keeps its own acceptance criteria and its already-queued triage packet — no new packet was created for it this pass.
37 remaining items
dezren39 commentedon Nov 2, 2026
agent-note:v1
event: picked_up
task: 5660 (repo-health-code-pilot, repo-worker-code — OpenChamber scheduled lane)
state: Claimed via atomic queue claim; resuming at awaiting_ci for PR #4331 opened by this queue task.
revalidation: Fresh metadata: #3192 is OPEN; PR #4331 is OPEN at prior pushed head 6f3e272. Required checks “basic: guard PR check list” and “native arm64: world snapshot determinism” are still QUEUED; local artifact tests passed 30/30 per checkpoint.
next: Watch checks for at most five minutes; if still pending, park and resume on fresh CI state.
dezren39 commentedon Nov 2, 2026
agent-note:v1
event: parked
task: 5660 (repo-health-code-pilot, repo-worker-code — OpenChamber scheduled lane)
state: Parked for 20 minutes after the five-minute CI watch.
artifact: PR #4331, current head 6f3e272.
ci: Required checks “basic: guard PR check list” and “native arm64: world snapshot determinism” remained pending; CodeRabbit and full nix check were skipped. Targeted local artifact tests passed 30/30.
resume: Revalidate PR/check state; merge only when required checks are green.
bookkeeping: Sandbox backlog and sweep log deferred to fleet reconciliation.
dezren39 commentedon Nov 2, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 2, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 2, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done — PR #4331 merged
SignedArtifact::parsed()now rejects non-NFC document media_type and tag name/value, reusing the existing NFC validation.cargo test --lib artifact::tests30 passed / 0 failed, rustfmt +git diff --checkclean. Codex review on head 6f3e272: no findings. PR CI checks still QUEUED in the runner backlog at merge; merged under the standing merge-authority rule.