Chronicle Integration

Chronicle is a versioned filesystem that provides each agent session with an isolated, content-addressable workspace. Every file change creates a new iteration, enabling clients to browse version history, view files at any point in time, and synchronize workspaces to local directories for editing in a native IDE.

Role in the Architecture

Chronicle sits behind Podium in the service hierarchy. When an agent modifies a file during a coding session, the change flows through Chronicle’s content-addressable storage. Clients access this data through Diminuendo’s file access messages, which are proxied through to Podium, which in turn queries Chronicle:
Client → Gateway → Podium → Chronicle
  list_files        GET /files         Content-addressable store
  read_file         GET /files/{path}  Versioned iterations
  file_history      GET /files/{path}/history
  file_at_iteration GET /files/{path}/at/{n}
The gateway does not communicate with Chronicle directly. All file operations are routed through Podium’s REST API, which manages the mapping between compute instances and their associated Chronicle workspaces.

File Versioning

Chronicle tracks every modification to every file in an agent’s workspace as a discrete iteration. An iteration is an immutable snapshot of a file at a specific point in time, identified by:
  • Iteration number — monotonically increasing integer, scoped to the file path within a workspace
  • Timestamp — epoch milliseconds when the change was persisted
  • Size — byte count of the file content at this iteration
  • Hash — content-addressable hash for deduplication and integrity verification
Clients can browse a file’s complete history using the file_history message and retrieve any past version using file_at_iteration:
// Get version history
const history = await client.fileHistory(sessionId, "src/auth.ts")
// [
//   { iteration: 1, timestamp: 1709312400000, size: 1234, hash: "abc..." },
//   { iteration: 2, timestamp: 1709312460000, size: 1456, hash: "def..." },
//   { iteration: 3, timestamp: 1709312520000, size: 1389, hash: "ghi..." },
// ]

// Read the file at iteration 1 (before the agent's changes)
const original = await client.fileAtIteration(sessionId, "src/auth.ts", 1)

Local-Sync Mode

Chronicle’s local-sync mode replaces traditional FUSE or NFS-based filesystem access with real files on the local filesystem, synchronized bidirectionally using platform-native file watching (FSEvents on macOS). This enables users to open agent workspace files in their preferred IDE while the agent is actively modifying them.
Local-sync is a desktop-only feature. It requires filesystem access from the Tauri Rust backend and is enabled via the local-sync Cargo feature flag in Chronicle.

Architecture

Local-sync consists of three cooperating components:

LocalMaterializer

Writes files from the upstream replication stream to the local filesystem. Implements the EventHandler trait. Includes echo suppression to prevent its own writes from being detected as local changes.

FsWatcher

Monitors the local directory for changes using notify::RecommendedWatcher (FSEvents on macOS). Changes are debounced and filtered to exclude editor temp files, .git directories, and other noise.

WriteJournal

Tracks all changes (both local and upstream) in a journal for replication. Provides the mechanism for conflict detection when both local and remote changes affect the same file.

Bidirectional Sync Flow

Upstream (agent changes)                     Local (user changes)
         │                                            │
    Replication stream                         FSEvents / inotify
         │                                            │
    LocalMaterializer                            FsWatcher
    (write to local)                        (detect local change)
         │                                            │
    Echo suppression ←────────────────────→ Change filtering
         │                                            │
    WriteJournal ─────────────────────────→ WriteJournal
         │                                            │
    Local filesystem ◄──────────────────── Local filesystem
Echo suppression is critical: when the LocalMaterializer writes a file, the FsWatcher will detect that write as a local change. Without suppression, this would create an infinite sync loop. The materializer registers each write in a short-lived “echo set,” and the watcher ignores any change events for paths currently in the echo set. Debouncing prevents rapid successive changes (common during agent file writes or IDE auto-save) from generating a flood of upstream sync operations. The watcher uses notify-debouncer-full with a configurable debounce interval.

Conflict Resolution

When both the agent (upstream) and the user (local) modify the same file within the same debounce window, a conflict is detected. The desktop client surfaces this conflict to the user via the Tauri IPC resolve_conflict command, presenting options to keep the local version, accept the upstream version, or merge manually.

Chronicle on macOS

On macOS, Chronicle’s local-sync mode uses the platform-native FSEvents API via notify::RecommendedWatcher. Files are materialized directly on APFS, so workspaces appear as real files in Finder and editors like VS Code. No FUSE layer or kernel extension is required. Key behaviors on macOS:
  • FSEvents-based watch for low-latency, native change detection
  • APFS-native files that behave like standard folders and can be edited directly
  • Echo suppression to avoid sync loops when Chronicle writes files that the watcher would otherwise detect
  • Tauri-managed lifecycle to start and stop local-sync per session from the desktop client

Tauri Integration

The desktop client provides four IPC commands for managing local-sync:
#[tauri::command]
async fn start_sync(session_id: String, local_dir: String) -> Result<(), String>;

#[tauri::command]
async fn stop_sync(session_id: String) -> Result<(), String>;

#[tauri::command]
async fn sync_status(session_id: String) -> Result<SyncStatus, String>;

#[tauri::command]
async fn resolve_conflict(
    session_id: String,
    path: String,
    resolution: ConflictResolution,
) -> Result<(), String>;
These commands are exposed to the React frontend through the TauriGatewayAdapter and can be invoked from the session management UI.

Gateway Integration

From the gateway’s perspective, Chronicle is transparent. The gateway proxies file access messages to Podium’s REST API without knowledge of Chronicle’s internal storage format or versioning mechanism:
Client MessageGateway Behavior
list_filesForward to PodiumClient.listFiles(instanceId, path, depth)
read_fileForward to PodiumClient.readFile(instanceId, path)
file_historyForward to PodiumClient.fileHistory(instanceId, path)
file_at_iterationForward to PodiumClient.fileAtIteration(instanceId, path, iteration)
All file operations require an active Podium instance for the session. If the session is inactive, the gateway must first activate it (creating a Podium instance) before file operations can proceed.

Building with Local-Sync

Chronicle’s local-sync feature is behind a Cargo feature flag:
# Build Chronicle with local-sync support
cargo build --no-default-features --features local-sync

# Run with local-sync enabled
chronicle /path/to/workspace --local-sync --ws-connect ws://podium:5082/sync
The local-sync feature pulls in the notify (v7) and notify-debouncer-full crates, which provide the platform-native file watching implementation.