Priority: P2 resource-bound correction.
Evidence
At #707 a8c322a02ce7f5453dab0a3d89393b9b729f6b4d, source-edit/src/digest.rs::persist_record calls serde_json::to_vec(value) before comparing its length with MAX_DURABLE_RECORD_BYTES (4 MiB). The bound protects what is published, but not serialization work or the allocation needed to reject an oversized journal. Recovery records can contain source preimages/intended content; JSON escaping also expands output relative to raw string length.
The read path already uses read_bounded; make the write-side bound equally real. No observed OOM, exploit, or promise that input is otherwise unbounded is claimed.
Change
Use the existing bounded writer primitive if one matches this contract, with serde_json::to_writer, so encoding stops before exceeding the output budget. A capped Vec writer preserves the current pre-publication validation ordering; a scratch-file writer is also possible only if it preserves exact temporary ownership and cleanup on every rejection. Do not grow a complete output Vec before checking it, pre-serialize solely to measure, or invent another journal format.
Keep the existing with_owned_temp_publish/private atomic replacement/directory-sync authority. An encoding refusal must leave the previous journal/receipt readable and unchanged; no partial serialized record may become authoritative. Map the limit failure distinctly from unrelated serialization or filesystem failures where the current error contract allows it.
Acceptance
At/below-limit records round-trip with identical canonical bytes. Oversized and heavily escaped content is rejected with bounded output allocation/work and without replacing an existing record. A serializer that fails midway also preserves the prior record and cleans only its owned scratch. Extend current durability tests; no daemon, huge corpus, or new persistence test framework is required for the serializer bound.
Source review plus maintained serializer API verification; no repository tests or benchmarks executed. Target #707; keep it draft.
Priority: P2 resource-bound correction.
Evidence
At #707
a8c322a02ce7f5453dab0a3d89393b9b729f6b4d, source-edit/src/digest.rs::persist_record callsserde_json::to_vec(value)before comparing its length with MAX_DURABLE_RECORD_BYTES (4 MiB). The bound protects what is published, but not serialization work or the allocation needed to reject an oversized journal. Recovery records can contain source preimages/intended content; JSON escaping also expands output relative to raw string length.The read path already uses
read_bounded; make the write-side bound equally real. No observed OOM, exploit, or promise that input is otherwise unbounded is claimed.Change
Use the existing bounded writer primitive if one matches this contract, with serde_json::to_writer, so encoding stops before exceeding the output budget. A capped Vec writer preserves the current pre-publication validation ordering; a scratch-file writer is also possible only if it preserves exact temporary ownership and cleanup on every rejection. Do not grow a complete output Vec before checking it, pre-serialize solely to measure, or invent another journal format.
Keep the existing with_owned_temp_publish/private atomic replacement/directory-sync authority. An encoding refusal must leave the previous journal/receipt readable and unchanged; no partial serialized record may become authoritative. Map the limit failure distinctly from unrelated serialization or filesystem failures where the current error contract allows it.
Acceptance
At/below-limit records round-trip with identical canonical bytes. Oversized and heavily escaped content is rejected with bounded output allocation/work and without replacing an existing record. A serializer that fails midway also preserves the prior record and cleans only its owned scratch. Extend current durability tests; no daemon, huge corpus, or new persistence test framework is required for the serializer bound.
Source review plus maintained serializer API verification; no repository tests or benchmarks executed. Target #707; keep it draft.