> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jesta.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Read detection

> How the agent notices when a honeytoken is read.

The endpoint agent is **pure Bash** - `curl` + `openssl` over POSIX `sh`, with no
Python runtime or third-party packages on the endpoint. It plants the bait, then
watches the bait paths for a read and fires a signed callback the moment one
happens.

## Sensors

| Platform | Sensor | Fidelity |
| - | - | - |
| **macOS** | `fs_usage`, pre-filtered with `grep` to just the bait paths | Real open/read events, **with process + user** |
| **Any** | `st_atime` poll fallback | Best-effort - many systems update atime lazily or not at all, so it can miss reads; no process/user |
| **Linux** | `auditd` / inotify `IN_ACCESS` | Planned - kernel-level read events |

`fs_usage` is the real sensor: it yields genuine open/read events and, via `ps`,
the process and user that read the file - which enrich the alert. It requires
root, which the agent has when run under MDM. The firehose is trimmed at the
source by `grep`-ing to only the bait paths, and noise processes (Spotlight,
`mds`, the agent itself, …) are filtered out. The atime fallback exists so
non-macOS hosts get *some* coverage, but it's strictly best-effort.

## The agent protocol

The agent never parses JSON. The agent-facing API speaks a plain-text protocol so
enroll, pull, and the signed callback are each a few lines of shell:

* **Enroll** - `POST /api/enroll` with the shared enroll token; the response is
  `key=value` lines, including the endpoint's `agent_token`.
* **Pull** - `GET /api/agent/deployments` returns this endpoint's own instances
  as tab-separated records (deployment id, path, HMAC secret, and URLs for the
  bait content and the callback). The bait content isn't inlined - it can be
  multi-line - so the agent fetches it raw from the per-deployment content URL.
* **Trigger** - on a read, the agent builds a `key=value` body
  (`deployment_id`, `event_type`, `process`, `pid`, `os_user`, `accessed_path`,
  `triggered_by`, `timestamp`) and POSTs it to `POST /api/trigger`.

Every callback is HMAC-signed: the agent computes
`openssl dgst -sha256 -hmac` over the exact request body and sends it as
`X-Thumper-Signature: sha256=<hmac>`. If the server stops recognizing the
deployment (a DB reset or redeploy returns `401`), the agent re-enrolls,
re-pulls, and resends - so the read that triggered it still alerts under fresh
credentials. See the [security model](/thumper/security-model) for how the server
verifies it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.