Severity: MED — correctness/security (dmc + systems lens)
Two wiring defects:
saveState() (outbox-log.js:488-491) appends to the journal only when !statePersistence, but start() passes both journalPath + storagePath, so the fast append-log is silently empty while the expensive snapshot runs.
- On restore,
applyState() (:524-601) and applyJournalEntry() (:649-713) re-check size/namespace limits but do not re-run verifyOutboxRecordSignature — the live append path gates sigs (:141-152), the restore path doesn't. Anyone who can write outboxlog-state.json/the journal injects rows attributed to any pubkey, loaded as authentic on boot. Trust silently shifts from writer-key to filesystem.
Fix: decide layering explicitly (journal-first + periodic snapshot; remove the !statePersistence guard), and re-run verifyOutboxRecordSignature during load (respect shouldVerifyAppend), dropping unverifiable rows. Tests: both-configured → append lands in journal; hand-edited row rejected on load.
Severity: MED — correctness/security (dmc + systems lens)
Two wiring defects:
saveState()(outbox-log.js:488-491) appends to the journal only when!statePersistence, butstart()passes bothjournalPath+storagePath, so the fast append-log is silently empty while the expensive snapshot runs.applyState()(:524-601) andapplyJournalEntry()(:649-713) re-check size/namespace limits but do not re-runverifyOutboxRecordSignature— the live append path gates sigs (:141-152), the restore path doesn't. Anyone who can writeoutboxlog-state.json/the journal injects rows attributed to any pubkey, loaded as authentic on boot. Trust silently shifts from writer-key to filesystem.Fix: decide layering explicitly (journal-first + periodic snapshot; remove the
!statePersistenceguard), and re-runverifyOutboxRecordSignatureduring load (respectshouldVerifyAppend), dropping unverifiable rows. Tests: both-configured → append lands in journal; hand-edited row rejected on load.