daemon
Background process that watches file changes, detects sensitive file reads, and auto-attests.
The Treeship daemon is a background process that watches your project tree for file changes and emits attestations or session events without requiring manual treeship attest calls. Two separate passes run in the main loop: a mtime pass for writes (auto-attestation) and an atime pass for sensitive file reads (session event emission).
treeship daemon start
Start the background daemon.
treeship daemon start
treeship daemon start --foreground
treeship daemon start --no-pushOptions
| Option | Description |
|---|---|
--foreground | Run in the foreground instead of backgrounding |
--no-push | Create local artifacts only, do not push to the hub |
The daemon reads .treeship/config.yaml once per cycle (every 2 seconds by default) so you can edit path rules without restarting.
treeship daemon stop
Stop the background daemon.
treeship daemon stoptreeship daemon status
Check whether the daemon is running.
treeship daemon statusFile write attestation
The default mode. When a file whose path matches a rule with on: write or on: change has its mtime advance, the daemon creates a signed attestation automatically.
Example config.yaml rule:
attest:
paths:
- path: "src/**"
on: "write"
label: "code change"
- path: "*lock*"
on: "change"
label: "dependency change"With these rules, editing src/main.rs or package-lock.json produces an attestation tagged with the configured label.
Sensitive file read detection
In addition to the write pass, the daemon runs a second pass that watches on: access rules for atime advancement. When a file matching such a rule is read, the daemon emits an agent.read_file event to the active session's event log (if a session is running) and, optionally, writes an ALERT line to daemon.log.
Example rule:
attest:
paths:
- path: "*.env*"
on: "access"
label: "env file access"
alert: trueThis default rule ships with treeship init, so .env, .env.local, .env.production, and friends are covered out of the box.
What gets watched
The regular file snapshot skips hidden dotfiles and dotdirectories (.git, .treeship, .next, and so on) to keep the watch set small. The sensitive-file pass adds two categories back in for on: access rules:
- Dotfiles at the project root.
.env,.env.local,.gitignore,.dockerignore, etc. - Known sensitive dot-directories, one level deep.
.aws/,.ssh/,.gnupg/,.docker/,.kube/. Files one level inside these are watched if they match anon: accessrule.
Event emission
When the daemon detects a read, it writes a SessionEvent of type agent.read_file to .treeship/sessions/<session_id>/events.jsonl with:
{
"session_id": "ssn_...",
"agent_instance_id": "daemon",
"agent_name": "treeship-daemon",
"agent_role": "watcher",
"event_type": {
"type": "agent.read_file",
"file_path": ".env",
"digest": "sha256:..."
},
"meta": {
"capture_confidence": "inferred",
"source": "daemon-atime",
"label": "env file access"
}
}The capture_confidence: "inferred" tag is deliberate. atime-based detection is not deterministic the way signed attestation is; it depends on the host filesystem's access-time policy. The receipt format preserves this confidence level so downstream consumers can weight the signal accordingly.
Alerts
If the matched rule has alert: true, the daemon also writes a line like:
[1744185720] ALERT: sensitive file accessed: .envto .treeship/daemon.log. Alerts are non-blocking; they are log-only and do not halt the daemon or any running process.
Behavior when no session is active
Read events require an active session to land in an event log. If the daemon detects a sensitive read while no session is running, it logs the detection (and the alert, if configured) to daemon.log but does not persist the event. Starting a session with treeship session start enables full capture.
Honest limitations
File-read detection is best-effort. The receipt format tags these events capture_confidence: "inferred" for real reasons:
noatimemounts. Filesystems mounted withnoatimenever update access times. Reads on these filesystems will not be detected.relatime(Linux default).relatimeonly updates atime if the previous atime is older than the current mtime, or if 24 hours have passed. A rapid burst of reads on the same file may only register once.- macOS variations. APFS typically updates atime, but specific configurations may not.
- Capture boundary. If a process bypasses the filesystem layer entirely (for example, by reading memory-mapped files without triggering the access-time hook), the daemon will not see it.
These are design boundaries, not bugs. The daemon captures the strongest possible signal from the OS and names the confidence level honestly in the event meta. See the Session Receipts concept page for the full framing.
Daemon log
The daemon writes structured log lines to .treeship/daemon.log. Useful entries:
[1744185600] daemon started (pid 48201)
[1744185710] attesting: src/main.rs (code change)
[1744185720] read event: .env (env file access)
[1744185720] ALERT: sensitive file accessed: .env
[1744185730] daemon stopping (pid file removed)The daemon respects TREESHIP_DISABLE=1. If set, the daemon starts but skips all attestation and event emission, which is useful for testing path rules without generating artifacts.