yuzu

filesystem

What it doesFilesystem operations — exists, list_dir, file_hash, search_dir, text ops (admin-only)
Version0.4.0
KindAction · mutating · gathered (device.filesystem.exists, device.filesystem.list_dir, device.filesystem.file_hash, device.filesystem.read, device.filesystem.create_temp, device.filesystem.create_temp_dir, device.filesystem.get_acl, device.filesystem.get_signature, device.filesystem.find_by_hash, device.filesystem.get_version_info, device.filesystem.search_dir, device.filesystem.search, device.filesystem.replace, device.filesystem.write_content, device.filesystem.append, device.filesystem.delete_lines, workflow.version_compliance_check)
PlatformsWindows ✅ · macOS ✅ · Linux ✅
Actionsappend (definition device.filesystem.append) · create_temp (definition device.filesystem.create_temp) · create_temp_dir (definition device.filesystem.create_temp_dir) · delete_lines (definition device.filesystem.delete_lines) · exists (definition device.filesystem.exists) · file_hash (definition device.filesystem.file_hash) · find_by_hash (definition device.filesystem.find_by_hash) · get_acl (definition device.filesystem.get_acl) · get_signature (definition device.filesystem.get_signature) · get_version_info (definition device.filesystem.get_version_info, workflow.version_compliance_check) · list_dir (definition device.filesystem.list_dir) · read (definition device.filesystem.read) · replace (definition device.filesystem.replace, workflow.config_search_and_replace) · search (definition device.filesystem.search) · search_dir (definition device.filesystem.search_dir) · write_content (definition device.filesystem.write_content)
Securityexists: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; list_dir: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; file_hash: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; create_temp: securable FileRetrieval · operation Write · risk Medium · dispatch Mutating · approval gate AdminOrApproval; create_temp_dir: securable FileRetrieval · operation Write · risk Medium · dispatch Mutating · approval gate AdminOrApproval; read: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; get_acl: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; get_signature: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; find_by_hash: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate AdminOrApproval; search_dir: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; get_version_info: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; search: securable FileRetrieval · operation Read · risk Low · dispatch ReadOnly · approval gate None; replace: securable FileRetrieval · operation Write · risk Medium · dispatch Mutating · approval gate AdminOrApproval; write_content: securable FileRetrieval · operation Write · risk Medium · dispatch Mutating · approval gate AdminOrApproval; append: securable FileRetrieval · operation Write · risk Medium · dispatch Mutating · approval gate AdminOrApproval; delete_lines: securable FileRetrieval · operation Delete · risk High · dispatch Destructive · approval gate AdminOrApproval
Rolesexecute: exists: endpoint-admin, endpoint-operator; list_dir: endpoint-admin, endpoint-operator; file_hash: endpoint-admin, endpoint-operator; read: endpoint-admin, endpoint-operator; create_temp: endpoint-admin; create_temp_dir: endpoint-admin; get_acl: endpoint-admin, endpoint-operator; get_signature: endpoint-admin, endpoint-operator; find_by_hash: endpoint-admin; get_version_info: endpoint-admin, endpoint-operator; search_dir: endpoint-admin, endpoint-operator; search: endpoint-admin, endpoint-operator; replace: endpoint-admin; write_content: endpoint-admin; append: endpoint-admin; delete_lines: endpoint-admin; replace: endpoint-admin; get_version_info: endpoint-admin, endpoint-operator · author: content-author

How it works

The filesystem plugin reads, inspects and edits files on the device the agent runs on. Its 16 actions fall into four groups: look (exists, list_dir, read, search, search_dir), identify (file_hash, find_by_hash, get_signature, get_version_info, get_acl), create (create_temp, create_temp_dir) and change (write_content, replace, append, delete_lines). Most actions turn the caller’s path into its canonical form, following symlinks, and, if the caller also passes base_dir, refuse a path that resolves outside that directory. The exceptions: find_by_hash and create_temp take no base_dir, and exists and a new-file write_content canonicalise only the parent directory when the target does not exist yet. Reads never alter the target. Changes to an existing file (replace, delete_lines, and write_content with overwrite=true) are written to a new sibling file and renamed over the target, so a reader sees either the old or the new content. append, write_content creating a new file, and the temp-file actions open the target directly. It is deliberately not a file manager: there is no whole-file delete, rename or copy, and no ACL write; get_acl only reads.

How the main actions work:

Only find_by_hash sets a typed result status, and only when it could not examine part of the tree or ran without its network-mount guard; every other outcome is carried in the rows and return code (see Result status).

flowchart LR
  OP["Operator or workflow"] --> SRV["Server: authz FileRetrieval Read, Write or Delete, gate None or AdminOrApproval"]
  SRV -- "gRPC mTLS" --> HOST["Agent plugin host"] --> EX["filesystem.execute"]
  EX --> VAL["validate_path: canonical path, optional base_dir"]
  VAL --> WIN["Windows leg: std::filesystem, BCrypt, WinVerifyTrust, version info, Win32 ACL"]
  VAL --> MAC["macOS leg: std::filesystem, shasum for file_hash, in-process SHA-256 for find_by_hash, codesign, plutil, ACL"]
  VAL --> LIN["Linux leg: std::filesystem, sha256sum and sha1sum for file_hash, in-process SHA-256 for find_by_hash, xattr ACL"]
  WIN --> ROWS["Pipe rows; a typed result status from find_by_hash only"]
  MAC --> ROWS
  LIN --> ROWS
  ROWS --> RS[("ResponseStore, 90 day default retention")] --> API["REST /api/responses, MCP execute_instruction"]

What you can decide with it

QuestionActionWhat you get and its limits
Is there a file or directory here, and how big?exists, list_direxists reports true or false, the type and size. list_dir returns one entry row per item and stops at 1000 entries with a warning| row.
What does this text file say?read, searchread returns 100 lines per call by default (limit, at most 10000) from a 1-based offset and refuses a file whose first 512 bytes contain a NUL byte; search returns 100 matching lines by default (max_matches, at most 10000). Both refuse a file over 100 MB.
Where is a directory or file named like this?search_dirName matches under a root: depth 5 by default (max_depth, at most 20), 100 results by default (max_results, at most 1000). It does not look inside files.
Is this the file I expect, or does a known file exist anywhere here?file_hash, find_by_hashSHA-256 or SHA-1 of one file (up to 10 GB). find_by_hash sweeps a directory tree for a SHA-256 within a fixed budget and says what it did not examine; see Caveat 2 for what a “no match” means.
Is it signed, and which version is it?get_signature, get_version_infoWindows and macOS only, with narrow meanings (Caveat 5). Linux has no equivalent and answers an error.
Who can touch it?get_aclPer-OS decode below.
Make a changewrite_content, replace, append, delete_lines, create_temp, create_temp_dirSee the safe workflow below. All need an administrator or an approval.

Safe workflow for changes. There is no backup and no undo: a change overwrites the file, and the old content is gone unless you kept a copy elsewhere. Work in this order: confirm the target with exists and read (or search to find the line); use replace with dry_run=true to see how many replacements it would make, since the other change actions have no preview; apply the change; verify by read, search or file_hash. Target rules: the path must already exist, except write_content with create=true, whose parent directory must exist. write_content refuses to touch an existing file unless overwrite=true, and refuses to create one unless create=true. replace, append and delete_lines need a regular file, and the text actions treat the file as lines (delete_lines rewrites the file so it ends with a newline, unless no lines remain). With regex=true, replace expands $1-style references in the replacement only when max_replacements is 0; with a limit it inserts the replacement text literally, so the same replacement string can behave differently with and without a limit.

get_acl by OS.

OSSourceWhat you seeNot shown
WindowsGetNamedSecurityInfoW owner and DACLsddl, control flags, one ace row per entry (allow, deny, conditional and object types; principal as DOMAIN\name or SID; hex access mask; inherit flags)The SACL (needs SeSecurityPrivilege); condition expressions are ignored. A NULL DACL (everyone allowed) prints no ace rows.
Linuxsystem.posix_acl_access and, for a directory, system.posix_acl_defaultowner, group, permissions, mode, acl|none|extended, one ace row per entryAn ACL over 64 KiB is an error. Only the first 64 distinct ids are resolved to names; the rest print as numbers.
macOSacl_get_file(ACL_TYPE_EXTENDED)owner, group, permissions, mode, acl row, one ace row per entry in stored orderNo extended ACL prints acl|none; a file system that cannot return ACLs prints acl|unsupported. An unresolved principal prints as its UUID.

On Linux and macOS the whole ACL is read before any row is written, so a failure is one error| row. On Windows an error part-way through the entries can follow rows already written.

Diagrams

Write path

sequenceDiagram
  participant S as Server
  participant P as filesystem plugin
  participant D as Disk
  S->>P: replace with path, search, replacement, optional base_dir
  P->>D: canonicalize path, check base_dir, regular file, size cap
  D-->>P: file bytes read into memory
  P->>P: match and substitute in memory
  alt dry_run is true or nothing matched
    P-->>S: replacements_made and sizes, file untouched
  else changes to write
    P->>D: create sibling temp exclusively, write, fsync, rename over target
    P-->>S: replacements_made and sizes
  end

replace, delete_lines and write_content on an existing file all follow the atomic path above. append and write_content on a new file skip the temp-and-rename step and write straight into the target, so an interrupted write can leave a partial file. The size cap is checked before anything is read, and the result is built entirely in memory.

find_by_hash

flowchart TD
  S["find_by_hash with directory, sha256, max_depth"] --> MT["Read the network-mount list first: Linux /proc/self/mountinfo, macOS getmntinfo_r_np, Windows the root's own spelling and drive type"]
  MT --> RM{"Is the directory as given on a network mount, a UNC path or a non-fixed drive?"}
  RM -->|"yes"| RX["Refused before anything touches it: skipped_network_mount, CONSTRAINED"]
  RM -->|"no"| V["validate_path: canonical directory; then max_depth is clamped to 1-10"]
  V --> RC{"Canonical root under a listed network mount (macOS, Linux), or not listable?"}
  RC -->|"yes"| RR["rc 0: skipped_network_mount=1 or skipped_unreadable=1, CONSTRAINED"]
  RR --> OUT
  RC -->|"no"| NX["Next entry"]
  NX --> B{"20 seconds or 50,000 entries reached, or the agent shutting down?"}
  B -->|"yes"| STOP["Stop with reason deadline, file_cap or cancelled"]
  B -->|"no"| K{"What is the entry, judged without following it?"}
  K -->|"under a network mount (macOS, Linux)"| SN["skipped_network_mount, not entered"]
  K -->|"symlink or junction"| SL["skipped_symlink, never followed"]
  K -->|"FIFO, device or socket"| SR["skipped_not_regular, never opened"]
  K -->|"directory"| D{"Can it be listed? On Linux: not a pseudo-filesystem such as /proc"}
  D -->|"no"| SU["skipped_unreadable"]
  D -->|"yes, or at max_depth: not opened, not counted"| NX
  K -->|"regular file"| O["Open no-follow; regular file and size decided on that descriptor or handle"]
  O -->|"over 10 GB, or larger than the bytes left in the budget"| SO["skipped_oversize; the second also makes the reason byte_cap"]
  O -->|"cannot open or read, cloud placeholder, unknown reparse tag"| SU
  O -->|"a link at open time"| SL
  O -->|"not a regular file at open time"| SR
  O -->|"ok"| H["Stream 64 KiB chunks through SHA-256, budget checked per chunk"]
  H -->|"equals the target"| M["match row; at 100 matches stop with max_results if entries remain"]
  H -->|"differs"| NX
  H -->|"a bound trips mid-file: byte_cap, deadline or cancelled"| STOP
  SN --> NX
  SL --> NX
  SR --> NX
  SU --> NX
  SO --> NX
  M --> NX
  NX -->|"none left"| OUT
  STOP --> OUT["Closing rows: network_mount_guard unavailable when the mount table could not be read, one truncated row when anything was skipped or the walk stopped, then matches_found"]

The mount check is a string comparison that runs before any call touching a path, because an open or listing on a dead hard-mounted server would hold one of the agent’s shared command workers with no deadline. Every skip the walk sees lands in a counter of the trailing truncated|<reason>|visited=N|hashed=N|skipped_unreadable=N|skipped_symlink=N|skipped_not_regular=N|skipped_oversize=N|skipped_network_mount=N row, written only when something was skipped or the walk stopped early (reason is none when no bound tripped). hashed= counts bytes admitted to the hasher, including a file abandoned part-way (no match is reported for it). Empty files are hashed. Stopping at 100 matches counts as a bound only when entries remained, so exactly 100 matches in a fully walked tree is clean. A search root that cannot be listed is not an error| line either: it answers return code 0 with skipped_unreadable=1 and CONSTRAINED. Caveat 2 lists the races that can still skip a directory uncounted.

How the “never followed” holds per OS. macOS opens every candidate with O_NOFOLLOW_ANY (no symlink in any path component) and turns the thread’s dataless-file materialisation off for the walk, so an iCloud or File Provider item that is not downloaded fails to open and is counted unreadable instead of being fetched. Linux 5.6 or later opens with openat2(RESOLVE_NO_SYMLINKS); on an older kernel, or where a seccomp profile denies openat2, it falls back to O_NOFOLLOW on the final component only, for the rest of the process’s life once openat2 has been refused (an architecture with no known openat2 number always uses the fallback). Windows opens the final component with FILE_FLAG_OPEN_REPARSE_POINT and classifies the handle before any read: a symlink, junction or other name-surrogate reparse point is a link, a compressed (WOF) or deduplicated file is hashed through a handle reopened without the reparse flag, and a cloud placeholder, an offline file or any other reparse tag is counted unreadable and never recalled. On Windows a mounted folder below the root is a reparse point and is skipped as a junction, and a \\?\ root reads as a UNC path and is refused. A relative directory is judged by its absolute spelling (the working directory joined lexically), so pass absolute paths.

OS capability

ActionWindowsmacOSLinux
append✅ supported · rung 1 · std::ofstream✅ supported · rung 1 · std::ofstream✅ supported · rung 1 · std::ofstream
create_temp✅ supported · rung 1 · yuzu_create_temp_file✅ supported · rung 1 · yuzu_create_temp_file✅ supported · rung 1 · yuzu_create_temp_file
create_temp_dir✅ supported · rung 1 · yuzu_create_temp_dir✅ supported · rung 1 · yuzu_create_temp_dir✅ supported · rung 1 · yuzu_create_temp_dir
delete_lines✅ supported · rung 1 · atomic_write_file✅ supported · rung 1 · atomic_write_file✅ supported · rung 1 · atomic_write_file
exists✅ supported · rung 1 · std::filesystem✅ supported · rung 1 · std::filesystem✅ supported · rung 1 · std::filesystem
file_hash✅ supported · rung 1 · bcrypt✅ supported · rung 2 · subprocess_runner:shasum✅ supported · rung 2 · subprocess_runner:sha256sum/sha1sum
find_by_hash✅ supported · rung 1 · std::filesystem+bcrypt✅ supported · rung 1 · std::filesystem+sha256_nofollow_fd✅ supported · rung 1 · std::filesystem+sha256_nofollow_fd
get_acl✅ supported · rung 1 · win32_acl✅ supported · rung 1 · posix_stat+acl_get_file(ACL_TYPE_EXTENDED)✅ supported · rung 1 · posix_stat+getxattr(system.posix_acl_*)
get_signature✅ supported · rung 1 · wintrust✅ supported · rung 2 · subprocess_runner:codesign⛔ unsupported
get_version_info✅ supported · rung 1 · win32_version_info✅ supported · rung 2 · subprocess_runner:plutil⛔ unsupported
list_dir✅ supported · rung 1 · std::filesystem✅ supported · rung 1 · std::filesystem✅ supported · rung 1 · std::filesystem
read✅ supported · rung 1 · std::ifstream✅ supported · rung 1 · std::ifstream✅ supported · rung 1 · std::ifstream
replace✅ supported · rung 1 · atomic_write_file✅ supported · rung 1 · atomic_write_file✅ supported · rung 1 · atomic_write_file
search✅ supported · rung 1 · std::ifstream+std::regex✅ supported · rung 1 · std::ifstream+std::regex✅ supported · rung 1 · std::ifstream+std::regex
search_dir✅ supported · rung 1 · std::filesystem+std::regex✅ supported · rung 1 · std::filesystem+std::regex✅ supported · rung 1 · std::filesystem+std::regex
write_content✅ supported · rung 1 · atomic_write_file✅ supported · rung 1 · atomic_write_file✅ supported · rung 1 · atomic_write_file

Privileges and prerequisites

OSRuns asExtra grant neededMeasuredIf the read is refused
WindowsLocalSystem today (docs/agent-privilege-model.md, tracked #1442; the target is the virtual service account NT SERVICE\YuzuAgent)None for any read action. write_content, replace, append and delete_lines against a path the running account cannot already write need that account to be an Administrators member; the default install grants nothing2026-10-04, bare metal, as LocalSystem (elevated)a single error|<message> line and return code 1 (for example error|GetNamedSecurityInfo failed (error N)); no typed status is set
macOSroot today: the shipped LaunchDaemon has no UserName key (docs/agent-privilege-model.md)None for any read action. The plugin never calls sudo; the agent runs as root today, so a write succeeds on any path the OS lets root write. Under the planned unprivileged account it would succeed only where that account can already write2026-10-04, bare metal, unprivileged at euid 501; the capture ran through LocalDispatcher, not the root daemonsame error|<message> and return code 1
Linuxyuzu-agent unprivileged system account on the packaged unit (docs/agent-privilege-model.md)None for any read action. The plugin never calls sudo: a write succeeds only on a path the yuzu-agent account can already write, and the OS refuses it on a system path2026-10-04, container, as euid 0 (root), so not the least-privilege yuzu-agent accountsame error|<message> and return code 1

Subprocesses: shasum (macOS) and sha256sum/sha1sum (Linux) for file_hash only (compute_hash_unix; find_by_hash starts none); /usr/bin/codesign for macOS get_signature (do_get_signature); /usr/bin/plutil for macOS get_version_info (do_get_version_info). Every one is invoked by absolute path with no shell through yuzu::agent::run_bounded_subprocess. No network access on any OS.

Who may run what is decided by the compiled capability row, not by the header’s role line. The plugin description says “admin-only”, but the read actions run for endpoint-operator as well as endpoint-admin; the write actions, create_temp, create_temp_dir and find_by_hash carry the AdminOrApproval gate. permissions.executeRoles in the definition YAML is documentation only and is not an access-control guarantee.

Data contract

Inputs

DefinitionParameterTypeRequiredDefaultConstraintsDescription
device.filesystem.appendpathstringyes-maxLength 4096The existing file to append content to.
device.filesystem.appendcontentstringno--The text content to append to the file.
device.filesystem.appendnewlinestringnotrueenum: true, falseIf “true” (default), adds a newline before appended content when the file does not end with a newline character. Prevents content from being joined to the last existing line.
device.filesystem.appendbase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.create_tempprefixstringnoyuzu-maxLength 64Prefix for the temp file name.
device.filesystem.create_tempsuffixstringno.tmpmaxLength 32Suffix/extension for the temp file name.
device.filesystem.create_tempdirectorystringno-maxLength 4096Optional. Create the temp file in this directory instead of the system default temp directory.
device.filesystem.create_temppersiststringnotrueenum: true, falseIf “true” (default), the file is kept. If “false”, the file is deleted after creation (useful for generating unique paths).
device.filesystem.create_temp_dirprefixstringnoyuzu-maxLength 64Prefix for the temp directory name.
device.filesystem.create_temp_dirdirectorystringno-maxLength 4096Optional. Create the temp directory inside this parent instead of the system default temp directory.
device.filesystem.create_temp_dirpersiststringnotrueenum: true, falseIf “true” (default), the directory is kept. If “false”, deleted after creation.
device.filesystem.delete_linespathstringyes-maxLength 4096The text file to delete lines from.
device.filesystem.delete_linesstart_lineint32yes-minimum 1The first line to delete (1-based, inclusive).
device.filesystem.delete_linesend_lineint32yes-minimum 1The last line to delete (1-based, inclusive). Clamped to total line count if it exceeds file length.
device.filesystem.delete_linesbase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.existspathstringyes-maxLength 4096The filesystem path to check.
device.filesystem.existsbase_dirstringno-maxLength 4096Optional. If set, the path must resolve within this directory. Prevents directory traversal outside the allowed subtree.
device.filesystem.file_hashpathstringyes-maxLength 4096The file to hash.
device.filesystem.file_hashalgorithmstringnosha256enum: sha256, sha1The hash algorithm to use.
device.filesystem.file_hashbase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.find_by_hashdirectorystringyes-maxLength 4096The root directory to search.
device.filesystem.find_by_hashsha256stringyes-minLength 64 · maxLength 64The SHA256 hex digest to search for (case-insensitive).
device.filesystem.find_by_hashmax_depthint32no3minimum 1 · maximum 10Maximum directory recursion depth. Default: 3, max: 10. Out-of-range values are rejected with a 400 by POST /api/instructions/{id}/execute.
device.filesystem.get_aclpathstringyes-maxLength 4096The file or directory to inspect.
device.filesystem.get_aclbase_dirstringno-maxLength 4096Optional. Restricts access to paths within this directory.
device.filesystem.get_signaturepathstringyes-maxLength 4096The executable, DLL, or app bundle to verify.
device.filesystem.get_signaturebase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.get_version_infopathstringyes-maxLength 4096The executable, DLL, or app bundle to inspect. On macOS this may be a .app bundle directory or an Info.plist file directly.
device.filesystem.get_version_infobase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.list_dirpathstringyes-maxLength 4096The directory to list.
device.filesystem.list_dirbase_dirstringno-maxLength 4096Optional. Restricts access to paths within this directory.
device.filesystem.readpathstringyes-maxLength 4096The text file to read.
device.filesystem.readoffsetint32no1minimum 11-based line number to start reading from. Default: 1.
device.filesystem.readlimitint32no100minimum 1 · maximum 10000Maximum number of lines to return. Default: 100, max: 10000. Out-of-range values are rejected with a 400 by POST /api/instructions/{id}/execute.
device.filesystem.readbase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.replacepathstringyes-maxLength 4096The file to perform find/replace on.
device.filesystem.replacesearchstringyes-maxLength 256The literal string or regex to search for.
device.filesystem.replacereplacementstringno-The text to replace matches with. Empty string removes matches.
device.filesystem.replaceregexstringnofalseenum: true, falseIf “true”, interpret search as an ECMAScript regex. Default: “false”.
device.filesystem.replacecase_sensitivestringnotrueenum: true, falseIf “true” (default), match is case-sensitive.
device.filesystem.replacedry_runstringnofalseenum: true, falseIf “true”, count replacements without modifying the file. Default: “false”.
device.filesystem.replacemax_replacementsint32no0minimum 0Maximum number of replacements to perform. 0 = unlimited. Default: 0.
device.filesystem.replacebase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.searchpathstringyes-maxLength 4096The text file to search.
device.filesystem.searchpatternstringyes-maxLength 256The literal string or regex to search for in each line.
device.filesystem.searchregexstringnofalseenum: true, falseIf “true”, interpret the pattern as an ECMAScript regex. Patterns with nested quantifiers are rejected to prevent catastrophic backtracking. Default: “false”.
device.filesystem.searchcase_sensitivestringnotrueenum: true, falseIf “true” (default), match is case-sensitive. If “false”, performs case-insensitive matching.
device.filesystem.searchmax_matchesint32no100minimum 1 · maximum 10000Maximum number of matching lines to return. Default: 100, max: 10000. Out-of-range values are rejected with a 400 by POST /api/instructions/{id}/execute.
device.filesystem.searchbase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
device.filesystem.search_dirrootstringyes-maxLength 4096The root directory to search from.
device.filesystem.search_dirpatternstringyes-maxLength 256Glob pattern (default) or regex to match against entry names. Examples: “.log”, “config”, “^backup-\d+$” (with regex=true).
device.filesystem.search_dirregexstringnofalseenum: true, falseIf “true”, interpret the pattern as an ECMAScript regex instead of a glob. Default: “false”.
device.filesystem.search_dirmatch_typestringnodirectoriesenum: directories, files, bothWhat to match: “directories” (default), “files”, or “both”.
device.filesystem.search_dirmax_depthint32no5minimum 1 · maximum 20Maximum directory recursion depth. Default: 5, max: 20. Out-of-range values are rejected with a 400 by POST /api/instructions/{id}/execute.
device.filesystem.search_dirmax_resultsint32no100minimum 1 · maximum 1000Maximum number of matching entries to return. Default: 100, max: 1000. Out-of-range values are rejected with a 400 by POST /api/instructions/{id}/execute.
device.filesystem.search_dirbase_dirstringno-maxLength 4096Optional. Restricts the search root to paths within this directory.
device.filesystem.write_contentpathstringyes-maxLength 4096The file path to write to. If the file does not exist, it will be created if create=true.
device.filesystem.write_contentcontentstringno--The text content to write to the file.
device.filesystem.write_contentcreatestringnofalseenum: true, falseIf “true”, create the file if it does not exist. Default: “false”.
device.filesystem.write_contentoverwritestringnofalseenum: true, falseIf “true”, overwrite an existing file. Default: “false”.
device.filesystem.write_contentbase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
workflow.config_search_and_replacepathstringyes-maxLength 4096Full path to the configuration file to update. Example: “/etc/myapp/config.ini” or “C:\ProgramData\MyApp\config.ini”
workflow.config_search_and_replacesearchstringyes-maxLength 256The current configuration value to search for. Matched literally (not regex) unless regex=true.
workflow.config_search_and_replacereplacementstringyes--The new configuration value to replace matches with.
workflow.config_search_and_replaceregexstringnofalseenum: true, falseIf “true”, interpret search as a regex pattern. Default: “false”.
workflow.config_search_and_replacecase_sensitivestringnotrueenum: true, falseIf “true” (default), match is case-sensitive.
workflow.config_search_and_replacedry_runstringnofalseenum: true, falseIf “true”, count replacements without modifying the file. Default: “false”.
workflow.config_search_and_replacemax_replacementsint32no0minimum 0Maximum number of replacements to perform. 0 = unlimited. Default: 0.
workflow.config_search_and_replacebase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.
workflow.version_compliance_checkpathstringyes-maxLength 4096Full path to the Windows executable to check. Example: “C:\Program Files\MyApp\myapp.exe”
workflow.version_compliance_checkminimum_versionstringno-maxLength 64The minimum acceptable product version string (e.g. “2.5.0.0”). Used by downstream PolicyFragment conditions for compliance evaluation. The check itself returns the raw version; the comparison happens in the policy layer using version_gte.
workflow.version_compliance_checkbase_dirstringno-maxLength 4096Optional. Restricts access to files within this directory.

Outputs

Every action writes pipe-delimited lines. A repeating row (list_dir’s entry rows, get_acl’s ace rows on every OS, search_dir’s result rows, find_by_hash’s match rows, read’s and search’s line rows) starts with a literal word naming its kind, then its fields; a single-value field is its own key|value line. There is no placeholder row for “found nothing”: a zero-hit action reports only its trailing count (total_matches|0, matches_found|0). find_by_hash adds up to two rows between its match rows and its count: network_mount_guard|unavailable when the mount table could not be read, then one truncated|... row when it skipped anything or stopped early; only match rows have the three fields a column matcher reads. A match path containing CR or LF shows a space in its place, | is escaped, and a trailing backslash becomes /. Failure ends with an error|<message> line and a non-zero return code, never a placeholder success row (see Result status), with two exceptions: get_signature reports its verdict in signature_status (including unknown and error_0x...) and get_version_info on macOS reports version_status|not_available, both with return code 0. An unknown action name writes unknown action: <name> with return code 1. For get_acl the ace row is ace|<type>|<principal>|<access>|<flags>, with - for an empty field and <access> as permission text on Linux and macOS but a hex access mask on Windows; acl|<none|extended|unsupported>|<acl flags or -> (Linux, macOS) and control|<flags or -> (Windows) are single-value rows.

Two places where the emitted rows and the declared columns differ, so match on the emitted names. delete_lines emits lines_deleted, total_lines_before and total_lines_after, while its definition declares lines_before and lines_after. append’s total_size is not the size after the append (see Caveat 3).

device.filesystem.append — status|bytes_appended|total_size

FieldTypeValuesAvailableExampleDescription
statusstringokWindows, Linux, macOSokOutcome of the append; the only value the plugin emits on success.
bytes_appendedint64-Windows, Linux, macOS21Number of bytes appended, including an inserted newline separator when newline=true and the file did not already end with one. Values: integer (bytes).
total_sizeint64-Windows, Linux, macOS40Total file size in bytes after the append. Values: integer (bytes).

device.filesystem.create_temp — path|persist

FieldTypeValuesAvailableExampleDescription
pathstring-Windows, Linux, macOSD:\yuzu-dev\tmp\yuzu-964a901bfe197402d93f51a7653b0697.tmpFull path to the created temporary file. Values: free text (path).
persistbool-Windows, Linux, macOStrueEchoes the persist parameter — whether the file was kept.

device.filesystem.create_temp_dir — path|persist

FieldTypeValuesAvailableExampleDescription
pathstring-Windows, Linux, macOSD:\yuzu-dev\tmp\yuzu-3849a33da8fc555d407ac0f2c67356f9Full path to the created temporary directory. Values: free text (path).
persistbool-Windows, Linux, macOStrueEchoes the persist parameter — whether the directory was kept.

device.filesystem.delete_lines — lines_deleted|lines_before|lines_after

FieldTypeValuesAvailableExampleDescription
lines_deletedint32-Windows, Linux, macOS1Count of lines removed (end_line - start_line + 1, after end_line is clamped to the file’s length). Values: integer.
lines_beforeint32-Windows, Linux, macOS2Declared here as lines_before, but the plugin’s actual wire field is total_lines_before (filesystem_plugin.cpp:1998) — the file’s total line count before the deletion. The emitted key does not match this column’s declared name; see the filesystem plugin README Caveats. Values: integer.
lines_afterint32-Windows, Linux, macOS1Declared here as lines_after, but the plugin’s actual wire field is total_lines_after (filesystem_plugin.cpp:1999) — the file’s total line count after the deletion. Same name mismatch as lines_before. Values: integer.

device.filesystem.exists — exists|type|size

FieldTypeValuesAvailableExampleDescription
existsbool-Windows, Linux, macOStrueWhether the path exists on the endpoint.
typestringfile directory otherWindows, Linux, macOSfileThe kind of filesystem entry found. Only emitted when exists is true.
sizeint64-Windows, Linux, macOS1405Size in bytes for a file entry; 0 for a directory or an “other” entry. Only emitted when exists is true. Values: integer (bytes).

device.filesystem.file_hash — hash|algorithm|size

FieldTypeValuesAvailableExampleDescription
hashstring-Windows, Linux, macOS9321feab332edbba521c7ea3eb978d9844cb4f62a4730dab9cf60fb79649037dHex-encoded digest of the file’s contents. Values: free text (hex digest).
algorithmstringsha256 sha1Windows, Linux, macOSsha256The hash algorithm actually used.
sizeint64-Windows, Linux, macOS1405File size in bytes at the time of hashing. Values: integer (bytes).

device.filesystem.find_by_hash — row_kind|path|size

FieldTypeValuesAvailableExampleDescription
row_kindstringmatch truncated matches_found network_mount_guardWindows, Linux, macOSmatchRow shape discriminator (the wire row’s leading tag). Values: match (one per file whose SHA-256 equals the target), truncated (at most one, only when something was skipped or the walk stopped early), matches_found (exactly one, always last), network_mount_guard (at most one, after any match rows and ahead of the other two, only when the mount table could not be read in full). Only match rows are data rows to column matchers; the other kinds have a different field count and never match.
pathstring-Windows, Linux, macOSC:\Windows\System32\drivers\etc\hostsmatch rows: full path of a file whose SHA-256 matches the target hash. (A truncated row carries its stop reason in this position — deadline, file_cap, byte_cap, cancelled, max_results, or none when no bound tripped — and a matches_found row the count; neither is a data row to a column matcher.)
sizeint64-Windows, Linux, macOS1405match rows: the matching file’s size in bytes. (A truncated row carries its counters here and in the fields after it, visited=N|hashed=N|skipped_unreadable=N|skipped_symlink=N|skipped_not_regular=N|skipped_oversize=N|skipped_network_mount=N, and matches_found|N is a two-field summary; neither is a data row to a column matcher.)

device.filesystem.get_acl — sddl|ace_type|account|access_mask|owner|group|permissions|mode|ace_perms|ace_flags|acl|acl_flags|control

FieldTypeValuesAvailableExampleDescription
sddlstring-WindowsO:BAD:AI(A;;FA;;;SY)(A;ID;FA;;;SY)(A;ID;FA;;;BA)Windows-only. SDDL string encoding the owner and DACL. Absent on POSIX. Values: free text (SDDL).
ace_typestringallow deny allow_conditional deny_conditional allow_object deny_object other user group maskWindows, Linux, macOSallowOne access-control-entry row’s kind; the plugin emits one ace row per entry on every OS. Windows - allow, deny, allow_conditional, deny_conditional, allow_object, deny_object, other (any other ACE type). macOS - allow or deny. Linux - user, group, mask or other (the POSIX ACL tag).
accountstring-Windows, Linux, macOSNT AUTHORITY\SYSTEMThe principal: DOMAIN\account or SID on Windows; :, :# or : (unresolved) on macOS, being user or group; the qualifier name or numeric id, or - for the owner/owning-group/mask/other entries on Linux. Values: free text.
access_maskstring-Windows0x001f01ffWindows-only. The ACE’s access mask, hex-formatted. Values: hex integer, or - for an ACE type the plugin does not decode.
ownerstring-Linux, macOSrootPOSIX-only. The file’s owning user name, or the numeric uid when it cannot be resolved. Values: free text.
groupstring-Linux, macOSwheelPOSIX-only. The file’s owning group name, or the numeric gid when it cannot be resolved. Values: free text.
permissionsstring-Linux, macOSrw-r--r--POSIX-only. rwx permission string for owner/group/other. Values: 9-character rwx string.
modestring-Linux, macOS0644POSIX-only. Octal permission bits. Values: 4-digit octal.
ace_permsstring-Linux, macOSr-xPOSIX-only. The ACE’s permissions: r/w/x triple on Linux, comma-separated ACL permission names on macOS (- when none). Values: free text.
ace_flagsstringobject_inherit container_inherit no_propagate inherit_only inherited file_inherit directory_inherit limit_inherit only_inherit default -Windows, Linux, macOS-The ACE’s flags, comma-joined, or - when none. Windows - object_inherit, container_inherit, no_propagate, inherit_only, inherited. macOS - inherited, file_inherit, directory_inherit, limit_inherit, only_inherit. Linux - default for an entry of the directory default ACL.
aclstringnone extended unsupportedLinux, macOSextendedPOSIX-only. Whether the path carries an extended ACL: none, extended, or unsupported (the filesystem cannot carry an ACL, not that none is set). Single-value row.
acl_flagsstringno_inherit defer_inherit -macOS-macOS-only. ACL-level flags, comma-joined, or - when none. Single-value field of the acl row.
controlstringprotected auto_inherited -WindowsprotectedWindows-only. DACL control flags from the security descriptor, comma-joined, or - when none. Single-value row.

device.filesystem.get_signature — signature_status

FieldTypeValuesAvailableExampleDescription
signature_statusstring-Windows, macOSunsignedSignature verdict. On Windows: valid/unsigned/distrusted/untrusted (Authenticode chain trust). On macOS: valid/unsigned/invalid/unknown — here valid means the code signature’s seal is intact (INTEGRITY only: unmodified since signing), NOT that the code is trusted or notarized; an ad-hoc/self-signed binary reports valid. Values: windows: valid, unsigned, distrusted, untrusted, security_settings_blocked, error_0x; macos: valid, unsigned, invalid, unknown.

device.filesystem.get_version_info — file_version|product_version|company_name|file_description|internal_name|original_filename|product_name|legal_copyright|version_status

FieldTypeValuesAvailableExampleDescription
file_versionstring-Windows, macOS6.2.26100.9278Windows: PE FileVersion (four-part). macOS: CFBundleVersion, the build number. Absent when not carried by the target. Values: free text (version string).
product_versionstring-Windows, macOS10.0.26100.9278Windows: PE ProductVersion (four-part). macOS: CFBundleShortVersionString, the marketing version. Absent when not carried by the target. Values: free text (version string).
company_namestring-WindowsMicrosoft CorporationWindows-only. PE CompanyName string resource, when present. Values: free text.
file_descriptionstring-WindowsNotepadWindows-only. PE FileDescription string resource, when present. Values: free text.
internal_namestring-WindowsNotepadWindows-only. PE InternalName string resource, when present. Values: free text.
original_filenamestring-WindowsNOTEPAD.EXE.MUIWindows-only. PE OriginalFilename string resource, when present. Values: free text.
product_namestring-WindowsMicrosoft® Windows® Operating SystemWindows-only. PE ProductName string resource, when present. Values: free text.
legal_copyrightstring-Windows© Microsoft Corporation. All rights reserved.Windows-only. PE LegalCopyright string resource, when present. Values: free text.
version_statusstringnot_availablemacOSnot_availablemacOS-only honest-empty marker: emitted as not_available when the target has no Info.plist or no version keys within it; absent otherwise (Windows never emits this field).

device.filesystem.list_dir — entry_name|entry_type|entry_size

FieldTypeValuesAvailableExampleDescription
entry_namestring-Windows, Linux, macOShostsFilename of one directory entry (not a full path). Values: free text.
entry_typestringfile directory symlink otherWindows, Linux, macOSfileThe entry’s kind.
entry_sizeint64-Windows, Linux, macOS1405Size in bytes; 0 for directories and symlinks. Values: integer (bytes).

device.filesystem.read — line_number|content|total_lines|file_size

FieldTypeValuesAvailableExampleDescription
line_numberint32-Windows, Linux, macOS11-based line number of this returned line. Values: integer.
contentstring-Windows, Linux, macOS# Copyright (c) 1993-2009 Microsoft Corp.The line’s text, with a trailing CRLF’s carriage return stripped. Values: free text.
total_linesint32-Windows, Linux, macOS37Total number of lines in the file, counted past the returned window. Values: integer.
file_sizeint64-Windows, Linux, macOS1405File size in bytes. Values: integer (bytes).

device.filesystem.replace — replacements_made|file_size_before|file_size_after|dry_run

FieldTypeValuesAvailableExampleDescription
replacements_madeint32-Windows, Linux, macOS2Count of replacements performed (or that would be performed, when dry_run=true). Values: integer.
file_size_beforeint64-Windows, Linux, macOS42File size in bytes before the replace. Values: integer (bytes).
file_size_afterint64-Windows, Linux, macOS18File size in bytes after the replace. Values: integer (bytes).
dry_runbool-Windows, Linux, macOSnot observed in any capture (every capture ran with dry_run=false)Present and true only when dry_run=true was requested; the plugin omits this line entirely otherwise. Values: true (field absent when dry_run=false).

device.filesystem.search — line_number|content|total_matches

FieldTypeValuesAvailableExampleDescription
line_numberint32-Windows, Linux, macOS191-based line number of a matching line. Values: integer.
contentstring-Windows, Linux, macOS# localhost name resolution is handled within DNS itself.Full text of the matching line, with a trailing carriage return stripped. Values: free text.
total_matchesint32-Windows, Linux, macOS3Count of matching lines returned in this result, capped by max_matches. Values: integer.

device.filesystem.search_dir — path|entry_type|total_matches

FieldTypeValuesAvailableExampleDescription
pathstring-Windows, Linux, macOSnot observed in any capture (every platform's capture returned total_matches|0 with no result rows)Absolute path of a matched file or directory.
entry_typestringfile directoryWindows, Linux, macOSnot observed in any capture (every platform's capture returned total_matches|0 with no result rows)Whether the matched entry is a file or a directory, as classified by match_type.
total_matchesint32-Windows, Linux, macOS0Count of matching entries returned in this result, capped by max_results. Values: integer.

device.filesystem.write_content — status|bytes_written|path

FieldTypeValuesAvailableExampleDescription
statusstringokWindows, Linux, macOSokOutcome of the write; the only value the plugin emits on success.
bytes_writtenint64-Windows, Linux, macOS20Number of bytes written to the file. Values: integer (bytes).
pathstring-Windows, Linux, macOSC:\Windows\Temp\yuzu_capture_bs7ucomo\yuzu_capture_tmp.txtCanonicalized path that was written.

workflow.config_search_and_replace — replacements_made|file_size_before|file_size_after

FieldTypeValuesAvailableExampleDescription
replacements_madeint32-all--
file_size_beforeint64-all--
file_size_afterint64-all--

workflow.version_compliance_check — file_version|product_version|company_name|product_name|file_description

FieldTypeValuesAvailableExampleDescription
file_versionstring-all--
product_versionstring-all--
company_namestring-all--
product_namestring-all--
file_descriptionstring-all--

Result status

StatusCompletenessProvenanceWhen
UNDECLAREDUNKNOWN(empty)every action other than find_by_hash, on every OS; and a find_by_hash run that hit no bound, read its mount table in full and skipped nothing, or skipped only by design (a symlink, a FIFO or device, a file over 10 GB: its truncated row still counts them). A refusal (bad path, missing parameter, wrong file type, tool failure) is one error|<message> output line with return code 1, so a caller must parse that line, not a status field, to detect failure
CONSTRAINEDPARTIALfilesystem:deadline, filesystem:file_cap, filesystem:byte_cap, filesystem:cancelled, filesystem:max_results, filesystem:mount_table_unreadable, filesystem:skipped_unreadable or filesystem:skipped_network_mount (the first that applies, in that order)find_by_hash only, return code 0: a bound tripped, the mount table could not be read in full so the network-mount guard was off, an entry or directory could not be read, or a network mount was refused

Reading a find_by_hash result. The status number is stored with the response and, when not zero, rides on the agent-transition event of the execution event stream (GET /api/v1/events), but the REST and MCP response rows and the CSV export do not carry it, and the completeness and provenance reach only the agent log (the wire carries the status enum alone, plugin_result_status in agent.proto). The rows are the signal you can see: the search is incomplete when the truncated reason is not none, when skipped_unreadable or skipped_network_mount is above zero, or when a network_mount_guard|unavailable row is present. matches_found|0 beside either row means “not found in the part examined”, never “not present”. A result-set column matcher takes only rows of the declared width, so it sees only match rows and cannot pick out the devices a search covered only in part: read the output rows for that. The bounds are fixed, not parameters: search a large tree one subdirectory at a time, and set stagger_seconds on a fleet-wide dispatch so every agent does not hash at once.

The truncated row’s counters:

The output never names a skipped path: when a counter is above zero, narrow it down by searching subdirectories.

Where the data goes

Sample output

Windows — captured: windows Windows 10.0.26200 x86_64 · bare-metal · 2026-10-04 · LocalSystem (elevated) · leg-hash 802f5467fb09

== action=exists path=C:\Windows\System32\drivers\etc\hosts
exists|true
type|file
size|1405
[result_status] UNDECLARED / UNKNOWN

== action=list_dir path=C:\Windows\System32\drivers\etc
entry|hosts|file|1405
entry|hosts.ics|file|444
entry|lmhosts.sam|file|3683
entry|networks|file|407
entry|protocol|file|1358
entry|services|file|17635
[result_status] UNDECLARED / UNKNOWN

== action=file_hash path=C:\Windows\System32\drivers\etc\hosts
hash|9321feab332edbba521c7ea3eb978d9844cb4f62a4730dab9cf60fb79649037d
algorithm|sha256
size|1405
[result_status] UNDECLARED / UNKNOWN

== action=create_temp
path|C:\WINDOWS\TEMP\yuzu-391d5e4c88ea2e641a93b5dd96849117.tmp
persist|true
[result_status] UNDECLARED / UNKNOWN

== action=create_temp_dir
path|C:\WINDOWS\TEMP\yuzu-41a05a87a8d5aae48423ac53334d0429
persist|true
[result_status] UNDECLARED / UNKNOWN

== action=read path=C:\Windows\System32\drivers\etc\hosts
line|1|# Copyright (c) 1993-2009 Microsoft Corp.
line|2|#
line|3|# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
line|4|#
line|5|# This file contains the mappings of IP addresses to host names. Each
line|6|# entry should be kept on an individual line. The IP address should
line|7|# be placed in the first column followed by the corresponding host name.
line|8|# The IP address and the host name should be separated by at least one
line|9|# space.
line|10|#
line|11|# Additionally, comments (such as these) may be inserted on individual
line|12|# lines or following the machine name denoted by a '#' symbol.
… 12 of 39 rows shown
[result_status] UNDECLARED / UNKNOWN

== action=get_acl path=C:\WINDOWS\TEMP\yuzu_acl_599f97e1\cond.txt
sddl|O:BAD:PAI(XA;;FA;;;WD;(Member_of {SID(BA)}))
control|protected,auto_inherited
ace|allow_conditional|\Everyone|0x001f01ff|-
[result_status] UNDECLARED / UNKNOWN

== action=get_signature path=C:\Windows\System32\notepad.exe
signature_status|unsigned
[result_status] UNDECLARED / UNKNOWN

== action=find_by_hash directory=C:\Windows\System32\drivers\etc sha256=031da282a9436540be78beab34801f32c1429491e881838b0ddf4f8c16e122cc
match|C:\Windows\System32\drivers\etc\hosts|1224
matches_found|1
[result_status] UNDECLARED / UNKNOWN

== action=search_dir root=C:\Windows\System32\drivers\etc pattern=*hosts*
total_matches|0
[result_status] UNDECLARED / UNKNOWN

== action=get_version_info path=C:\Windows\System32\notepad.exe
file_version|6.2.26100.9278
product_version|10.0.26100.9278
company_name|Microsoft Corporation
file_description|Notepad
internal_name|Notepad
original_filename|NOTEPAD.EXE.MUI
product_name|Microsoft® Windows® Operating System
legal_copyright|© Microsoft Corporation. All rights reserved.
[result_status] UNDECLARED / UNKNOWN

== action=search path=C:\Windows\System32\drivers\etc\hosts pattern=localhost
match|19|# localhost name resolution is handled within DNS itself.
match|20|#	127.0.0.1       localhost
match|21|#	::1             localhost
total_matches|3
[result_status] UNDECLARED / UNKNOWN

== action=replace path=C:\WINDOWS\TEMP\yuzu_capture_599f97e1\yuzu_capture_tmp.txt search=yuzu-capture replace=yuzu-replaced
replacements_made|1
file_size_before|24
file_size_after|12
[result_status] UNDECLARED / UNKNOWN

== action=write_content path=C:\WINDOWS\TEMP\yuzu_capture_599f97e1\yuzu_capture_tmp.txt content="yuzu-capture written" overwrite=true
status|ok
bytes_written|20
path|C:\Windows\Temp\yuzu_capture_599f97e1\yuzu_capture_tmp.txt
[result_status] UNDECLARED / UNKNOWN

== action=append path=C:\WINDOWS\TEMP\yuzu_capture_599f97e1\yuzu_capture_tmp.txt content="yuzu-capture appended"
status|ok
bytes_appended|22
total_size|20
[result_status] UNDECLARED / UNKNOWN

== action=delete_lines path=C:\WINDOWS\TEMP\yuzu_capture_599f97e1\yuzu_capture_tmp.txt start_line=1 end_line=1
lines_deleted|1
total_lines_before|2
total_lines_after|1
[result_status] UNDECLARED / UNKNOWN

macOS — captured: macos macOS 26.6.2 arm64 · bare-metal · 2026-10-04 · euid 501 (jsmith) · leg-hash 802f5467fb09

== action=exists path=/etc/hosts
exists|true
type|file
size|213
[result_status] UNDECLARED / UNKNOWN

== action=list_dir path=/etc
entry|manpaths|file|36
entry|rc.common|file|1560
entry|auto_master|file|195
entry|csh.login|file|121
entry|syslog.conf|file|96
entry|krb5.keytab|file|1946
entry|sudoers.d|directory|0
entry|ssl|directory|0
entry|csh.logout|file|39
entry|aliases.db|file|16384
entry|bashrc_Apple_Terminal|file|9293
entry|yuzu|directory|0
… 12 of 76 rows shown
[result_status] UNDECLARED / UNKNOWN

== action=file_hash path=/etc/hosts
hash|c7dd0e2ed261ce76d76f852596c5b54026b9a894fa481381ffd399b556c0e2da
algorithm|sha256
size|213
[result_status] UNDECLARED / UNKNOWN

== action=create_temp
path|/var/folders/hq/lc3t_rsx2blfc8rhys6kzh4r0000gn/T//yuzu-oqRhjd.tmp
persist|true
[result_status] UNDECLARED / UNKNOWN

== action=create_temp_dir
path|/var/folders/hq/lc3t_rsx2blfc8rhys6kzh4r0000gn/T//yuzu-sPr64l
persist|true
[result_status] UNDECLARED / UNKNOWN

== action=read path=/etc/hosts
line|1|##
line|2|# Host Database
line|3|#
line|4|# localhost is used to configure the loopback interface
line|5|# when the system is booting.  Do not change this entry.
line|6|##
line|7|127.0.0.1	localhost
line|8|255.255.255.255	broadcasthost
line|9|::1             localhost
total_lines|9
file_size|213
[result_status] UNDECLARED / UNKNOWN

== action=get_acl path=/tmp/yuzu_test_acl_Rw0R2f/acl_file
owner|jsmith
group|wheel
permissions|rw-r--r--
mode|0644
acl|extended|-
ace|deny|group:staff|delete|-
ace|allow|user:_www|read,write|file_inherit
[result_status] UNDECLARED / UNKNOWN

== action=get_signature path=/bin/ls
signature_status|valid
[result_status] UNDECLARED / UNKNOWN

== action=find_by_hash directory=/etc sha256=c7dd0e2ed261ce76d76f852596c5b54026b9a894fa481381ffd399b556c0e2da
match|/private/etc/hosts|213
truncated|none|visited=261|hashed=2997701|skipped_unreadable=17|skipped_symlink=4|skipped_not_regular=0|skipped_oversize=0|skipped_network_mount=0
matches_found|1
[result_status] CONSTRAINED / PARTIAL / filesystem:skipped_unreadable

== action=search_dir root=/etc pattern=*hosts*
total_matches|0
[result_status] UNDECLARED / UNKNOWN

== action=get_version_info path=/bin/ls
version_status|not_available
[result_status] UNDECLARED / UNKNOWN

== action=search path=/etc/hosts pattern=localhost
match|4|# localhost is used to configure the loopback interface
match|7|127.0.0.1	localhost
match|9|::1             localhost
total_matches|3
[result_status] UNDECLARED / UNKNOWN

== action=replace path=/var/folders/hq/lc3t_rsx2blfc8rhys6kzh4r0000gn/T//yuzu_capture_MaI1z1B3/yuzu_capture_tmp.txt search=yuzu-capture replace=yuzu-replaced
replacements_made|2
file_size_before|37
file_size_after|13
[result_status] UNDECLARED / UNKNOWN

== action=write_content path=/var/folders/hq/lc3t_rsx2blfc8rhys6kzh4r0000gn/T//yuzu_capture_MaI1z1B3/yuzu_capture_tmp.txt content="yuzu-capture written" overwrite=true
status|ok
bytes_written|20
path|/private/var/folders/hq/lc3t_rsx2blfc8rhys6kzh4r0000gn/T/yuzu_capture_MaI1z1B3/yuzu_capture_tmp.txt
[result_status] UNDECLARED / UNKNOWN

== action=append path=/var/folders/hq/lc3t_rsx2blfc8rhys6kzh4r0000gn/T//yuzu_capture_MaI1z1B3/yuzu_capture_tmp.txt content="yuzu-capture appended"
status|ok
bytes_appended|22
total_size|20
[result_status] UNDECLARED / UNKNOWN

== action=delete_lines path=/var/folders/hq/lc3t_rsx2blfc8rhys6kzh4r0000gn/T//yuzu_capture_MaI1z1B3/yuzu_capture_tmp.txt start_line=1 end_line=1
lines_deleted|1
total_lines_before|2
total_lines_after|1
[result_status] UNDECLARED / UNKNOWN

Linux — captured: linux Debian GNU/Linux 13 (trixie) aarch64 · container · 2026-10-04 · euid 0 · leg-hash 802f5467fb09

== action=exists path=/etc/hosts
exists|true
type|file
size|177
[result_status] UNDECLARED / UNKNOWN

== action=list_dir path=/etc
entry|nsswitch.conf|file|494
entry|update-motd.d|directory|0
entry|dpkg|directory|0
entry|skel|directory|0
entry|rc1.d|directory|0
entry|xattr.conf|file|681
entry|fstab|file|37
entry|logrotate.d|directory|0
entry|libaudit.conf|file|191
entry|os-release|file|286
entry|pam.d|directory|0
entry|issue.net|file|20
… 12 of 82 rows shown
[result_status] UNDECLARED / UNKNOWN

== action=file_hash path=/etc/hosts
hash|5ed49ae76ca451e9f063a9b28d11f6e8784a851e983664cb495206b75453c412
algorithm|sha256
size|177
[result_status] UNDECLARED / UNKNOWN

== action=create_temp
path|/tmp/yuzu-GqGF5B.tmp
persist|true
[result_status] UNDECLARED / UNKNOWN

== action=create_temp_dir
path|/tmp/yuzu-CtDWtd
persist|true
[result_status] UNDECLARED / UNKNOWN

== action=read path=/etc/hosts
line|1|127.0.0.1	localhost
line|2|::1	localhost ip6-localhost ip6-loopback
line|3|fe00::	ip6-localnet
line|4|ff00::	ip6-mcastprefix
line|5|ff02::1	ip6-allnodes
line|6|ff02::2	ip6-allrouters
line|7|172.17.0.3	yuzu-capture-host
total_lines|7
file_size|177
[result_status] UNDECLARED / UNKNOWN

== action=get_acl path=/tmp/yuzu_test_acl/f
owner|root
group|root
permissions|rw-rwxr--
mode|0674
acl|extended|-
ace|user|-|rw-|-
ace|user|1001|rw-|-
ace|group|-|r--|-
ace|group|1002|r-x|-
ace|mask|-|rwx|-
ace|other|-|r--|-
[result_status] UNDECLARED / UNKNOWN

== action=get_signature path=/bin/ls
error|code signature verification is not supported on this platform
[result_status] UNDECLARED / UNKNOWN
[rc] 1

== action=find_by_hash directory=/etc sha256=ed9ab7f958d4c01aa81e80a108e2c69db299b3e7cd6577b286905f1ee4c47b4c
match|/etc/hosts|413
truncated|none|visited=1676|hashed=1922709|skipped_unreadable=0|skipped_symlink=720|skipped_not_regular=0|skipped_oversize=0|skipped_network_mount=0
matches_found|1
[result_status] UNDECLARED / UNKNOWN

== action=search_dir root=/etc pattern=*hosts*
total_matches|0
[result_status] UNDECLARED / UNKNOWN

== action=get_version_info path=/bin/ls
error|version info extraction is not supported on this platform
[result_status] UNDECLARED / UNKNOWN
[rc] 1

== action=search path=/etc/hosts pattern=localhost
match|1|127.0.0.1	localhost
match|2|::1	localhost ip6-localhost ip6-loopback
total_matches|2
[result_status] UNDECLARED / UNKNOWN

== action=replace path=/tmp/yuzu_capture_vySwPCm6/yuzu_capture_tmp.txt search=yuzu-capture replace=yuzu-replaced
replacements_made|2
file_size_before|37
file_size_after|13
[result_status] UNDECLARED / UNKNOWN

== action=write_content path=/tmp/yuzu_capture_vySwPCm6/yuzu_capture_tmp.txt content="yuzu-capture written" overwrite=true
status|ok
bytes_written|20
path|/tmp/yuzu_capture_vySwPCm6/yuzu_capture_tmp.txt
[result_status] UNDECLARED / UNKNOWN

== action=append path=/tmp/yuzu_capture_vySwPCm6/yuzu_capture_tmp.txt content="yuzu-capture appended"
status|ok
bytes_appended|22
total_size|20
[result_status] UNDECLARED / UNKNOWN

== action=delete_lines path=/tmp/yuzu_capture_vySwPCm6/yuzu_capture_tmp.txt start_line=1 end_line=1
lines_deleted|1
total_lines_before|2
total_lines_after|1
[result_status] UNDECLARED / UNKNOWN

Caveats and known gaps

  1. Size and count limits apply, and most stop without a marker. read, search, replace and delete_lines refuse a file over 100 MB, and write_content refuses content over 100 MB, each with an error| row; file_hash refuses a file over 10 GB; append has no size check. list_dir stops at 1000 entries and says so with a warning| row, but search_dir (default 100 results, at most 1000) and search (default 100 lines, at most 10000) simply stop at their cap, so a count equal to the cap in force (the default unless you raised it) means there may be more.
  2. find_by_hash is a bounded sweep, not proof of absence. Whichever of 20 seconds, 50,000 entries visited or 100 matches comes first ends the walk, at most 4 GiB is hashed (a file larger than what is left is skipped and the reason becomes byte_cap), it goes at most 10 levels deep (max_depth, default 3; the agent reads a value below 1 as 1, above 10 as 10 and one with no leading integer as 3), and these bounds are fixed in the plugin. What it skipped is counted in the truncated row, but matches_found|0 still means only “not found in the part examined” (Result status). The deadline is checked between system calls, so a single blocked read or directory listing is not interrupted, and only the agent’s shutdown cancels a running search, never a single command (#4875). Residuals: on Windows, and on Linux without openat2 (a kernel before 5.6, or a seccomp profile that denies it), only the final path component is opened no-follow (#6348); a directory whose permissions change after the readability check is skipped without being counted (a symlink swapped in between that check’s access() and its no-follow directory open is refused there and counted unreadable); on macOS a directory swapped for a symlink between its classification and its descent is listed (names only: each child then fails its own no-follow open) and can block on a dead mount. The mount guard is a deny-list of filesystem names read once at the start, so a stacked filesystem, a bare fuse mount and a mount made mid-walk are not caught, and a root reached through a symlink, or spelt in a different letter case on macOS, reaches validate_path before the canonical re-check can refuse it (#4875). Windows hands the walk no mount list: only the root’s own spelling and drive type are judged, so a root that is itself a link to a share is not refused. When the mount table cannot be read in full (an unreadable or over-16 MiB /proc/self/mountinfo, a failed getmntinfo_r_np) the walk runs unguarded, logs once and reports network_mount_guard|unavailable and CONSTRAINED. A Windows drive letter that does not exist is answered directory is not accessible, not as a mount. An agent older than this release still runs the unbounded, symlink-following walk.
  3. append reports a stale total_size. The size is read while the written bytes are still buffered, so on the captured 20-byte file it reported total_size|20 after appending 22 bytes; treat it as the size before the append and use exists or read to get the real size. Because the same check runs before the buffer is flushed, a write failure at flush time (for example a full disk) can still print status|ok.
  4. base_dir is the caller’s own narrowing, and path checks leave a small race. It is an optional parameter on each request, not an agent-side policy, so a request that omits it is not restricted, and find_by_hash and create_temp do not take it at all. The re-validation just before codesign and plutil narrows but cannot close the window in which a writer inside base_dir swaps the path for a symlink, so base_dir must not be writable by lower-privileged principals.
  5. Signature and version answers are narrow. On Windows get_signature verifies only an embedded Authenticode signature and does not check revocation, so a file signed only through a Windows catalog is expected to report unsigned; the captured sample shows notepad.exe reporting unsigned, which is consistent with that (the cause is inferred, not shown). On macOS valid means the code signature’s seal is intact, not that the code is trusted or notarized, and a timed-out or truncated codesign or plutil run is reported as unknown or not_available rather than as a failure.

Source and tests