Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Three-State Model

The engine keeps three views of every file under management, plus a persistent record of what was last applied. Comparing the four is what makes conflict detection and safe interactive resolution possible.

The four stores

The engine keeps four views of every file under management. The arrows below describe how data flows from one store to the next during an apply.

SOURCE                 TARGET                  DESTINATION            PERSISTENT
(filesystem)           (in memory)             (filesystem)           (redb)

.bashrc.j2     ───►    .bashrc (rendered) ──►  ~/.bashrc       ◄──►  blake3(.bashrc)
key.txt.age    ───►    key.txt (decrypted)──►  ~/key.txt      ◄──►  blake3(key.txt)
                     decrypt + render        write + chmod          record

Source     →  Target     : decrypt .age, then render .j2
Target     →  Destination: write to disk, apply mode
Target     ↔  Persistent : hash target, store in db
Target     ↔  Destination: three-way compare to detect Synced/Modified/Conflict

The bold arrows show the writes the apply loop performs. The plain arrows are reads that drive the comparison.

StoreWhere it livesMutable?Notes
SourceFiles in the source repository (filesystem)Read-only during applyFilenames encode extensions (.j2, .age, .j2.age); file mode bits come from metadata().mode().
TargetRendered, decrypted content (in memory)Recomputed on demandAlways the desired post-apply state for a given source.
DestinationThe actual files on the user’s machine (filesystem)Read + WriteWhere the user’s dotfiles actually live.
Persistentredb database at <source>/.guisu-state.dbWritten after a successful applyContent hash + mode of the last applied target.

Status types

For each file under management, the engine computes a status by comparing target, destination, and database:

TargetDestinationDatabaseStatusDefault action
AAASyncedSkip
ABAModified (by you)Overwrite
AABModified (in source)Apply
ABCConflictPrompt (--interactive) or overwrite
AAddedCreate
BBRemovedDelete
BAModified + RemovedConflict

Entry types

crates/engine/src/entry.rs defines three enums and a struct:

#![allow(unused)]
fn main() {
pub enum SourceEntry {
    File { source_path, target_path, attributes },
    Directory { source_path, target_path, attributes },
    Symlink { source_path, target_path, link_target },
}

pub enum TargetEntry {
    File { path, content: Vec<u8>, mode: Option<u32> },
    Directory { path, mode: Option<u32> },
    Symlink { path, target: PathBuf },
    Remove { path },
}

pub struct DestEntry {
    pub path: RelPath,
    pub kind: EntryKind,
    pub content: Option<Vec<u8>>,
    pub mode: Option<u32>,
    pub link_target: Option<PathBuf>,
}

pub enum EntryKind {
    File,
    Directory,
    Symlink,
    Missing,
}
}

SourceEntry and TargetEntry differ slightly because the source carries parsed attributes while the target carries concrete content.

Destinations may also receive a remove directive from Metadata::remove (declared in .guisu/state.toml); see User Guide — File Attributes. The apply step processes removes in a separate pre-pass before applying targets.

File attributes

FileAttributes in crates/engine/src/attr.rs is a plain struct:

#![allow(unused)]
fn main() {
pub struct FileAttributes {
    pub is_template: bool,    // .j2 extension
    pub is_encrypted: bool,   // .age extension
    pub mode: Option<u32>,     // source file's metadata().mode()
}
}

is_template and is_encrypted are decoded from the filename extension at read time. mode is read from the source file’s metadata().mode() and propagated to the destination by apply. The permission-related bitflags (private_ / readonly_ / executable_ / dot_ / exact_) and the entry-type prefixes (modify_ / remove_ / symlink_) are no longer recognized. See User Guide — File Attributes.

See also