Direct answer: establish the capture path before interpreting a file row

AgentSight v1.0.35 has two materially different file-open collection paths. The modern Linux probe attaches an fexit handler to security_file_open, uses bpf_d_path to resolve the file path, and ignores rejected opens and non-regular files at that hook. If the required hook or helper is unavailable, the loader falls back to syscall-entry observation and reports a fallback notice on stderr. That older path can report failed attempts, relative filenames, and 60-second userspace aggregation.

Therefore a modern FILE_OPEN with write access establishes that the process successfully opened a regular file for a write-capable operation. It does not establish that the process wrote bytes, changed the file's contents, or was the only process that touched the path. Conversely, an absent event is not proof of absent access.

This is a source-level examination of product release v1.0.35 at commit c1574366122a90c451bca6a84713e28813ab95e2, not a new measurement of the current kernel or a claim of complete file-system coverage.

The fexit hook filters on success and file type

The BPF function trace_file_open_exit first checks the hook return value and the configured tracking scope. It rejects kernel threads and inodes that are not regular files, and suppresses special namespace-related file systems. An allowed open then receives the PID, command, monotonic timestamp, access classification, file identity, and resolved-path attempt.

The path is resolved in the opener's mount namespace. That is valuable for container debugging, but it is not a guarantee that a path with the same spelling identifies the same inode in a different namespace. Stable file identity needs device/inode and layer context in addition to a display string.

Access flags are derived from FMODE_READ, FMODE_WRITE, and executable-mode or executable-flag cases. The code handles newer __FMODE_EXEC-style flags and older encodings. These classify an open, not a complete sequence of reads, writes, or execution outcomes.

Evidence matrix: eight cases that change the interpretation

ScenarioModern probe behaviorSafe inference
Successful open of a regular file, first matching access in the processA resolved-path FILE_OPEN event is eligible after security_file_open returns success.The file was opened with the reported access; this is not a record of bytes read or written.
Second open of the same inode, mount/dentry, access, process execution and overlay layerUsually suppressed by the BPF LRU deduplication map.One row does not mean the file was opened only once; LRU eviction can also cause another row.
A read followed by a write-capable open of the same fileDifferent access bits produce a different deduplication key.A write-capable open is evidence of access mode, not proof that content changed.
Open denied before security_file_open succeeds, or a path that does not existNo successful regular-file-open event from the modern fexit hook.The syscall-entry fallback may still show attempts; do not mix the two semantics.
Executable file and its ELF interpreterExecutable opens are eligible and marked using mode/flag checks.An executable open alone does not establish what the program subsequently did.
Overlay backing-layer accessLayer identity contributes to the key; some runtime-origin layer opens are suppressed.A layer row and logical file row must not be naively counted as two user actions.
A path with invalid UTF-8, a long path, or bpf_d_path failureThe display path may be replaced/truncated, or blank with path_error.Use filepath_hex where present and preserve path_error; do not treat an empty path as root.
Ring-buffer reservation failure or BPF recursion missA FILE_OPEN_LOST counter records missing events.The count is a loss signal, not a way to reconstruct the missing paths.

These entries describe the implemented capture contract, not a statistical probability that any particular file will appear in a future run.

Why duplicate suppression changes counts

The kernel map file_opens_seen is an LRU hash with 65,536 entries. Its key combines process identity and execution lifetime, the mount and dentry, inode, access bits, and overlay-layer status. If a matching key is already present, the hook suppresses the next event. Different access bits or a changed process execution identity can create a new entry; capacity pressure may evict an entry and allow the same file to be emitted again.

This is why counting FILE_OPEN rows as exact open syscall frequency is incorrect. The output is intentionally de-duplicated evidence. The syscall-entry fallback applies a different, 60-second userspace aggregation contract, so the row counts from two hosts are not directly interchangeable.

Paths, overlays, and loss: preserve the uncertainty fields

The modern path buffer holds up to 4,095 bytes of resolved path text. Invalid UTF-8 is rendered with replacement characters while filepath_hex preserves original captured bytes; path_error identifies a failed resolution. Consumers must not silently normalize replacement characters into a definitive filesystem path, and a blank filepath with path_erroris a failed lookup, not evidence that the process opened a file named /.

The event also reports layer, dev, and ino. Overlay backing-layer accesses may have different identity than their logical paths. The hook deliberately suppresses some overlay-layer opens performed with initial-namespace mount credentials to avoid attributing runtime implementation effects to the observed workload.

Ring-buffer allocation failure and BPF recursion misses can produce FILE_OPEN_LOST counters. Those counters establish that observation was incomplete; they cannot recover which paths were missed. Together with filtering, timing, and deduplication, this makes a blank audit an absence of observed evidence, not evidence of absence.

Reproduce the interpretation with a bounded local task

On a Linux test machine with an appropriately built AgentSight process probe and authorization to load eBPF, run the process tracer and deliberately open one controlled regular file with read access and then write-capable access. Repeat the read, attempt to open a nonexistent name, and execute a simple ELF program. Compare event access flags, file identity, duplicates, and stderr before drawing conclusions. The following invokes the documented debug wrapper:

# Terminal A: scope intentionally broad for a disposable test machine
sudo ./agentsight debug process -- -m 0

# Terminal B: use a unique scratch directory, without private data
tmpdir=$(mktemp -d)
printf 'seed\n' > "$tmpdir/sample"
cat "$tmpdir/sample" >/dev/null
cat "$tmpdir/sample" >/dev/null
printf 'change\n' >> "$tmpdir/sample"
cat "$tmpdir/does-not-exist" >/dev/null 2>&1 || true
/usr/bin/true
rm -rf -- "$tmpdir"

Inspect the JSONL stream for FILE_OPEN, FILE_OPEN_LOST, filepath, path_error, access, layer, dev, and ino. Check stderr for a syscall fallback announcement. A broad -m 0 trace can include unrelated system processes: use only a disposable environment and narrow the scope for real work. No result from this proposed reproduction was collected in this article.

What has actually been tested in public?

The implementation record in product PR #227 reports a focused live hook harness on Linux 7.3.0 release-candidate kernel. It saw one controlled write, one deduplicated read, suppressed a failed open, and captured executable and interpreter file opens. That is the author's bounded validation, not an independent retest here.

The same PR explicitly states that the full process runtime suite could not attach on that workspace because the required process tracefs event was unavailable; a separate network tracing test encountered an existing verifier rejection. These qualifications matter more than claiming the resolved-path hook works on every supported Linux machine. The current product repository retains a process runtime regression harness for environments able to attach the BPF programs.

Use file evidence with the other observation layers

Agent-native session history may explain the agent's declared tool step, but it does not independently confirm each kernel file effect. Conversely, a process file-open record cannot identify why a particular prompt caused a tool call without additional correlation. Use the audit provenance reference to distinguish live events from reconstructed rows, and the PID namespace scope analysis when deciding which container tasks were eligible to be observed.

If you are deciding whether to use eBPF rather than native session files, start with the evidence-mode guide. For installation and supported command flags, use the canonical AgentSight documentation. Recorded captures may contain sensitive file paths, command lines, prompts, or network evidence; keep raw reports out of public issue descriptions.

Primary sources and scope

This reference pins implementation claims to AgentSight v1.0.35 rather than generalizing from an undated current build. Confirm the configured capture hook, effective filters, kernel capabilities, and event-loss counters on the machine whose audit you are reviewing.