Skip to content

Filesystem Analysis & Disk Cleanup

Filesystem Analysis & Disk Cleanup provides deep visibility into what is consuming disk space across your fleet and gives you safe, auditable tools to reclaim it. The system combines a parallel-scanning agent worker pool, resumable scan state, and a preview-before-execute cleanup workflow to help administrators identify and resolve disk space issues at scale – without risking accidental data loss.

The feature operates in two phases: analysis (scan the filesystem and build a snapshot of what is consuming space) and cleanup (preview safe candidates, then execute deletion of approved items). Both phases are available through the REST API, the dashboard UI, and the AI assistant.


Disk space exhaustion is one of the most common and disruptive issues in managed fleets. A single device running out of disk can cause application crashes, failed updates, corrupted databases, and user complaints. Across a fleet of thousands of devices, the problem multiplies – temp files accumulate silently, browser caches grow unchecked, old downloads pile up, and log files go unrotated.

Filesystem Analysis addresses this by:

  • Scanning deeply with a parallel worker pool that can traverse millions of filesystem entries efficiently.
  • Resuming interrupted scans so that long-running analysis on large volumes is not lost if the agent restarts or the connection drops.
  • Categorizing disk usage into actionable groups: largest files, largest directories, temp file accumulation, old downloads, unrotated logs, trash usage, and duplicate candidates.
  • Providing safe cleanup with a strict preview-then-execute workflow that only targets known-safe categories.

A filesystem scan is initiated by sending a POST request to the scan endpoint with a target path and optional configuration. The API queues a filesystem_analysis command to the agent via WebSocket (preferred for immediate dispatch) or the command queue.

The agent-side scanner uses a worker pool to parallelize directory traversal. Multiple goroutines walk the filesystem concurrently, each processing a subtree and reporting results back to a coordinator. This is critical for scanning large volumes – a serial scan of a 2TB disk with millions of files can take over an hour, while a parallelized scan with 8-16 workers can complete in minutes.

The system supports three scan strategies that control how much of the filesystem is traversed:

Strategy Behavior When to Use
baseline Full recursive scan from the specified path. Scans every directory up to maxDepth. First scan on a device, or when disk usage has changed significantly.
incremental Scans only “hot directories” – directories that changed significantly since the last baseline. Routine follow-up scans to detect new accumulation without rescanning the entire volume.
auto The API automatically selects baseline or incremental based on scan state. Default. Recommended for most use cases.

When using auto strategy, the API evaluates the following conditions to determine the scan mode:

  1. If the scan targets a non-root path (not C:\ or /), a baseline scan is always used.

  2. If a previous scan was interrupted and checkpoint data exists, a baseline scan resumes from the checkpoint.

  3. If no baseline has ever been completed for this device, a baseline scan is started.

  4. If the current disk usage percentage has changed by more than 3% since the last baseline, a baseline scan is triggered.

  5. If hot directories exist from the previous scan, an incremental scan targets only those directories.

Long-running baseline scans store checkpoint data in the device_filesystem_scan_state table. The checkpoint records which directories have been scanned and which are still pending. If a scan is interrupted (agent restart, network drop, timeout), the next scan with auto or baseline strategy will automatically resume from the checkpoint rather than starting over.

The scan state table tracks:

Field Type Description
deviceId UUID Primary key. One state record per device.
lastRunMode string baseline or incremental.
lastBaselineCompletedAt timestamp When the last full baseline scan finished.
lastDiskUsedPercent real Disk usage percentage at the time of the last baseline. Used to decide when a full rescan is needed.
checkpoint JSONB Pending directories and depth information for resume.
aggregate JSONB Accumulated scan results from completed subtrees.
hotDirectories JSONB Directories identified as high-churn for incremental scans (up to 24).

The scan endpoint accepts the following parameters:

Terminal window
POST /api/v1/devices/:deviceId/filesystem/scan
Content-Type: application/json
{
"path": "C:\\",
"strategy": "auto",
"maxDepth": 32,
"topFiles": 50,
"topDirs": 30,
"maxEntries": 5000000,
"workers": 8,
"timeoutSeconds": 300,
"followSymlinks": false
}
Field Type Default Description
path string Absolute path to scan. Required. Max 2048 characters.
strategy enum auto Scan strategy: auto, baseline, or incremental.
maxDepth number Maximum directory depth to traverse (1-64).
topFiles number Number of largest files to return (1-500).
topDirs number Number of largest directories to return (1-200).
maxEntries number Maximum filesystem entries to scan (1,000 to 25,000,000).
workers number Number of parallel scan workers (1-32).
timeoutSeconds number 300 (baseline) / 120 (incremental) Scan timeout in seconds (5-900).
followSymlinks boolean Whether to follow symbolic links during traversal.

The response returns a 202 Accepted with the command ID and scan mode:

{
"success": true,
"data": {
"commandId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "queued",
"createdAt": "2026-02-20T14:30:00.000Z",
"scanMode": "baseline",
"strategy": "auto"
}
}

When a scan completes, the results are saved as a filesystem snapshot in the device_filesystem_snapshots table. Snapshots are immutable records of the filesystem state at a point in time.

Field Type Description
id UUID Unique snapshot identifier.
deviceId UUID The device that was scanned.
capturedAt timestamp When the snapshot was created.
trigger enum on_demand (user-initiated) or threshold (automatic).
partial boolean true if the scan was interrupted or incomplete.
summary JSONB Aggregate statistics (files scanned, dirs scanned, bytes scanned, max depth, permission denied count).
largestFiles JSONB Top largest files by size (up to 50).
largestDirs JSONB Top largest directories by size (up to 30).
tempAccumulation JSONB Temporary file accumulation by category (bytes per category).
oldDownloads JSONB Files in download directories that have not been accessed recently (up to 200).
unrotatedLogs JSONB Log files that have grown large without rotation (up to 200).
trashUsage JSONB Files in trash/recycle bin (up to 16).
duplicateCandidates JSONB Files that appear to be duplicates based on size and name (up to 200).
cleanupCandidates JSONB All items eligible for safe cleanup (up to 1,000).
errors JSONB Errors encountered during scanning (permission denied, etc., up to 200).
Terminal window
GET /api/v1/devices/:deviceId/filesystem

Returns the most recent snapshot for the device. The response restructures the snapshot data for readability:

{
"data": {
"id": "snapshot-uuid",
"deviceId": "device-uuid",
"capturedAt": "2026-02-20T14:35:00.000Z",
"trigger": "on_demand",
"partial": false,
"scanMode": "baseline",
"path": "C:\\",
"summary": {
"filesScanned": 1250000,
"dirsScanned": 85000,
"bytesScanned": 450000000000,
"maxDepthReached": 24,
"permissionDeniedCount": 12
},
"topLargestFiles": [...],
"topLargestDirectories": [...],
"tempAccumulation": [
{ "category": "browser_cache", "bytes": 2500000000 },
{ "category": "temp_files", "bytes": 1800000000 }
],
"oldDownloads": [...],
"unrotatedLogs": [...],
"trashUsage": [...],
"duplicateCandidates": [...],
"cleanupCandidates": [...],
"errors": []
}
}

When incremental scans run after a baseline, the results are merged with the existing snapshot data. The merge logic:

  • Accumulates summary counts (files scanned, dirs scanned, bytes scanned, permission denied count).
  • Takes the maximum for max depth reached.
  • Deduplicates largest files and directories by path, keeping the entry with the larger size.
  • Merges temp accumulation by category, summing byte counts.
  • Deduplicates cleanup candidates, old downloads, unrotated logs, and trash entries by path.
  • Concatenates error lists up to a cap of 200.

Disk cleanup follows a strict two-phase workflow: preview first, then execute. This ensures that no files are deleted without explicit review.

Only items in the following categories are eligible for automated cleanup:

Category Description Examples
temp_files Operating system and application temporary files. %TEMP%, /tmp, app-specific temp directories.
browser_cache Web browser cache files. Chrome, Firefox, Edge, Safari cache directories.
package_cache Package manager caches. npm, pip, Homebrew, Chocolatey, NuGet cache directories.
trash Files in the trash or recycle bin. Windows Recycle Bin, macOS Trash, Linux trash directories.

The preview builds a cleanup plan from the latest filesystem snapshot without touching any files on the device.

Terminal window
POST /api/v1/devices/:deviceId/filesystem/cleanup-preview
Content-Type: application/json
{
"categories": ["temp_files", "browser_cache"]
}
Field Type Required Description
categories array No Filter to specific categories. If omitted, all safe categories are included. Max 10 entries.

The response includes the preview plan and creates a cleanup run record with status previewed:

{
"success": true,
"data": {
"cleanupRunId": "run-uuid",
"snapshotId": "snapshot-uuid",
"estimatedBytes": 4300000000,
"candidateCount": 156,
"categories": [
{ "category": "temp_files", "count": 89, "estimatedBytes": 1800000000 },
{ "category": "browser_cache", "count": 67, "estimatedBytes": 2500000000 }
],
"candidates": [
{
"path": "C:\\Users\\admin\\AppData\\Local\\Temp\\old_installer.exe",
"category": "temp_files",
"sizeBytes": 450000000,
"safe": true,
"reason": "Temporary file older than 30 days",
"modifiedAt": "2026-01-15T10:00:00.000Z"
}
]
}
}

After reviewing the preview, submit the specific paths you want to delete. Only paths that appear in the latest preview as safe candidates are accepted.

Terminal window
POST /api/v1/devices/:deviceId/filesystem/cleanup-execute
Content-Type: application/json
{
"paths": [
"C:\\Users\\admin\\AppData\\Local\\Temp\\old_installer.exe",
"C:\\Users\\admin\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cache"
]
}
Field Type Required Description
paths array Yes Paths to delete. Must be from the latest previewable candidates. Min 1, max 200 entries. Max 4096 characters per path.

The API validates each path against the current snapshot’s safe cleanup candidates. Paths not present in the candidate list are silently excluded. For each valid path, a file_delete command is dispatched to the agent with recursive: true.

The response reports per-path results:

{
"success": true,
"data": {
"cleanupRunId": "run-uuid",
"status": "executed",
"bytesReclaimed": 4100000000,
"selectedCount": 2,
"failedCount": 0,
"actions": [
{
"path": "C:\\Users\\admin\\AppData\\Local\\Temp\\old_installer.exe",
"category": "temp_files",
"sizeBytes": 450000000,
"status": "completed"
},
{
"path": "C:\\Users\\admin\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cache",
"category": "browser_cache",
"sizeBytes": 3650000000,
"status": "completed"
}
]
}
}

If all cleanup actions fail, the run status is failed. If at least one succeeds, the status is executed.


The AI assistant provides two tools for filesystem analysis and cleanup.

A read-only tool that retrieves the latest filesystem snapshot for a device and explains what is consuming disk space. It can optionally trigger a fresh scan.

Parameter Type Description
deviceId string (UUID) The device to analyze. Required.
refresh boolean If true, trigger a new scan before returning results.
path string Root path to scan (default: OS root).
maxDepth number Maximum scan depth (1-64).
topFiles number Number of largest files to return (1-500).
topDirs number Number of largest directories to return (1-200).
maxEntries number Maximum filesystem entries to scan (1,000-25,000,000).
workers number Number of parallel scan workers (1-32).
timeoutSeconds number Scan timeout (5-900 seconds).
maxCandidates number Maximum cleanup candidates to return (1-200).

The AI uses this tool to answer questions like “What is using disk space on this device?”, “Why is the C: drive full?”, and “Show me the largest files on device X.”

disk_cleanup (Tier 1 preview / Tier 3 execute)

Section titled “disk_cleanup (Tier 1 preview / Tier 3 execute)”

A dual-tier tool. Preview mode is Tier 1 (auto-executed, read-only). Execute mode is Tier 3 (requires human approval before deleting files).

Parameter Type Description
deviceId string (UUID) The device to clean up. Required.
action enum preview or execute. Required.
categories array Filter cleanup to specific categories. Optional.
paths array Specific paths to delete (required for execute). Min 1, max 200.
maxCandidates number Maximum candidates to return in preview (1-200).

The filesystem scanner identifies several categories of reclaimable space. Here is what each category covers and where the scanner looks.

  • %TEMP% (user temp directory)
  • C:\Windows\Temp (system temp directory)
  • C:\Windows\Prefetch (prefetch cache)
  • Application-specific temp directories

Cache directories for all major browsers:

  • Chrome: User Data/Default/Cache, Code Cache, Service Worker
  • Firefox: cache2 directory in profile
  • Edge: Same structure as Chrome (Chromium-based)
  • Safari: ~/Library/Caches/com.apple.Safari
  • npm: ~/.npm/_cacache
  • pip: ~/.cache/pip (Linux/macOS) or %LOCALAPPDATA%\pip\cache (Windows)
  • Homebrew: ~/Library/Caches/Homebrew
  • Chocolatey: C:\ProgramData\chocolatey\cache
  • NuGet: ~/.nuget/packages
  • Windows: C:\$Recycle.Bin per-user directories
  • macOS: ~/.Trash
  • Linux: ~/.local/share/Trash

Additional Detected Items (Not Auto-Cleaned)

Section titled “Additional Detected Items (Not Auto-Cleaned)”

The scanner also detects the following, which appear in the snapshot but are not eligible for automated cleanup:

  • Old downloads: Files in download directories older than a configurable threshold.
  • Unrotated logs: Log files that have grown beyond expected sizes.
  • Duplicate candidates: Files with matching sizes and names in different locations.
  • Large files: The top N largest individual files on the volume.

These items require manual review and are surfaced in the snapshot for informational purposes.


Table: device_filesystem_snapshots

Column Type Description
id UUID Primary key.
device_id UUID Foreign key to devices.
captured_at timestamp When the snapshot was created.
trigger enum on_demand or threshold.
partial boolean Whether the scan was incomplete.
summary JSONB Aggregate scan statistics.
largest_files JSONB Top largest files.
largest_dirs JSONB Top largest directories.
temp_accumulation JSONB Temp file accumulation by category.
old_downloads JSONB Old files in download directories.
unrotated_logs JSONB Large unrotated log files.
trash_usage JSONB Trash/recycle bin contents.
duplicate_candidates JSONB Potential duplicate files.
cleanup_candidates JSONB All safe cleanup candidates.
errors JSONB Scan errors.
raw_payload JSONB Complete raw agent response.

Indexed on (device_id, captured_at) for efficient latest-snapshot queries.

Table: device_filesystem_cleanup_runs

Column Type Description
id UUID Primary key.
device_id UUID Foreign key to devices.
requested_by UUID Foreign key to users (who initiated the cleanup).
requested_at timestamp When the cleanup was requested.
approved_at timestamp When the cleanup was approved for execution (null for preview-only runs).
plan JSONB The cleanup plan (snapshot ID, categories, preview data).
executed_actions JSONB Per-path execution results.
bytes_reclaimed bigint Total bytes reclaimed by the cleanup.
status enum previewed, executed, or failed.
error text Error message if the run failed.

Indexed on (device_id, requested_at) for history queries.

Table: device_filesystem_scan_state

Column Type Description
device_id UUID Primary key. Foreign key to devices. One row per device.
last_run_mode text baseline or incremental.
last_baseline_completed_at timestamp When the last full baseline finished.
last_disk_used_percent real Disk usage at last baseline (for delta detection).
checkpoint JSONB Pending directories for scan resume.
aggregate JSONB Accumulated partial results.
hot_directories JSONB High-churn directories for incremental scans.

All filesystem endpoints are prefixed with /api/v1/devices/:deviceId. Replace :deviceId with a valid device UUID.

Method Path Description Permission
GET /devices/:id/filesystem Get latest filesystem analysis snapshot devices.read
POST /devices/:id/filesystem/scan Trigger a filesystem scan devices.execute
POST /devices/:id/filesystem/cleanup-preview Preview safe cleanup candidates devices.read
POST /devices/:id/filesystem/cleanup-execute Execute cleanup on approved paths devices.execute

“No filesystem analysis available yet” (404)

Section titled ““No filesystem analysis available yet” (404)”

No snapshot exists for this device. The device has not been scanned yet. Trigger a scan with POST /devices/:id/filesystem/scan and wait for it to complete before querying the snapshot.

“No filesystem snapshot available. Run a scan first.” (404 on cleanup-preview)

Section titled ““No filesystem snapshot available. Run a scan first.” (404 on cleanup-preview)”

The cleanup preview requires an existing snapshot to build the candidate list from. Run a filesystem scan first, then retry the preview.

Large filesystem scans can exceed the default command timeout. Try:

  • Increasing timeoutSeconds (up to 900 seconds / 15 minutes).
  • Reducing maxEntries to limit the scan scope.
  • Reducing maxDepth to avoid deeply nested directory trees.
  • Scanning a subdirectory instead of the root path.

The scan uses resumable state, so a timed-out baseline scan will resume from its checkpoint on the next attempt.

The preview filters candidates by two criteria: the safe flag must be true AND the category must be one of the four safe categories (temp_files, browser_cache, package_cache, trash). If the snapshot contains cleanup candidates but the preview returns none, the candidates may be in non-safe categories (e.g., old downloads, unrotated logs) that require manual review.

Also verify that the categories filter in your request matches the categories present in the snapshot’s candidates.

“No valid cleanup paths selected from latest previewable candidates” (400)

Section titled ““No valid cleanup paths selected from latest previewable candidates” (400)”

The paths submitted to the execute endpoint do not match any current safe cleanup candidates. This can happen if:

  • A new scan ran between the preview and execute, changing the candidate list.
  • The paths were manually constructed rather than copied from a preview response.
  • The candidates’ safe flag was false (items outside safe categories are excluded).

Re-run the preview and use paths from the fresh response.

The cleanup execute response reports per-path status. Individual paths can fail if:

  • The file was already deleted between preview and execute.
  • The agent does not have permission to delete the file (e.g., a file locked by a running process).
  • The path is on a read-only filesystem.

The overall run status is executed if at least one path succeeded, or failed if every path failed. The bytesReclaimed field reflects only the successfully deleted paths.

Incremental scans only traverse hot directories identified by the previous baseline. If no hot directories were detected (the disk is relatively stable), the incremental scan has little to scan. This is expected behavior. Run a baseline scan to get a comprehensive view.

Scan shows “partial: true” in the snapshot

Section titled “Scan shows “partial: true” in the snapshot”

The scan was interrupted before completing. This can be caused by agent restart, network disconnection, or timeout. The partial snapshot contains valid data for the portions that were scanned. Run another scan to resume from the checkpoint – the system will merge results with the partial data.

The agent process may not have permission to read certain directories (e.g., other users’ home directories, system-protected paths). These are logged in the snapshot’s errors array with the path and error type. Running the agent as root/SYSTEM reduces permission errors but is not always necessary – the most valuable disk usage data is typically in user-accessible directories.