Treeship
CLI reference

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-push

Options

OptionDescription
--foregroundRun in the foreground instead of backgrounding
--no-pushCreate 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 stop

treeship daemon status

Check whether the daemon is running.

treeship daemon status

File 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: true

This 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:

  1. Dotfiles at the project root. .env, .env.local, .gitignore, .dockerignore, etc.
  2. Known sensitive dot-directories, one level deep. .aws/, .ssh/, .gnupg/, .docker/, .kube/. Files one level inside these are watched if they match an on: access rule.

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: .env

to .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:

  • noatime mounts. Filesystems mounted with noatime never update access times. Reads on these filesystems will not be detected.
  • relatime (Linux default). relatime only 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.