Skip to content

quality: atomic file writes for ~/.deepseek/ — config, sessions, audit, queue #355

@Hmbown

Description

@Hmbown

Problem

The TUI persists several critical files that can be corrupted by an interrupted write (Ctrl+C mid-save, kernel panic, OOM kill):

A partial write to any of these results in a corrupt JSON file on disk that fails to parse on next startup. We saw a related fix recently (persistence actor in #310 / commit f1f601c) for the blocking problem; the atomicity problem is separate.

Fix

All persisted writes go through a write_atomic(path, contents) helper:

pub fn write_atomic(path: &Path, contents: &[u8]) -> io::Result<()> {
    let parent = path.parent().ok_or_else(...)?;
    let tmp = NamedTempFile::new_in(parent)?;
    fs::write(tmp.path(), contents)?;
    tmp.as_file().sync_all()?; // fsync the temp
    tmp.persist(path)?; // atomic rename
    // optionally fsync the parent directory on Linux for full durability
    Ok(())
}

Use tempfile::NamedTempFile::new_in(parent) (already in workspace via reqwest's deps probably; verify) so the rename is on the same filesystem.

For append-only files (audit.log):

  • Open with OpenOptions::append(true).
  • Buffer writes; flush + fsync after each batch.
  • For tamper-resistance (companion idea, separate issue): hash-chain entries so a missing/altered line is detectable.

Apply to

Audit every fs::write\|File::create\|.write_all site in crates/tui/src/ that touches ~/.deepseek/. Convert to write_atomic (or for append-only, use the buffered-flush pattern).

Acceptance criteria

  • write_atomic helper exists in crates/tui/src/utils.rs.
  • All non-append persisted files use it.
  • Append-only audit.log uses buffered flush + fsync.
  • Test: simulate crash mid-write (write 50% of content, then panic in a child process); verify the original file is intact and no corrupt file exists.
  • Standard verification gates pass.

Companion to: #310 (persistence actor — non-blocking) + this (atomic).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions