Endpoint coverage and threat model¶
Preloop combines bounded endpoint inventory with evidence from configured clients and routed requests. It does not see every agent on every machine. Choose the scope of a pilot explicitly: OS, app/version, user/device population, model route, business MCP service and permitted data. An inventory result is not proof that every action on that device is governed.
This guide describes merged source at
726d71a3899a,
reviewed on 2026-10-22. Source references below pin that revision; check the
actual CLI/server versions in your deployment. A source feature or passing
fixture does not establish availability in a particular installer, edition,
vendor tenant or app version. See discovery reporting
for commands and staged deployment recipes for rollout.
What each signal proves¶
Use these four states independently. They are evidence categories, not a progression in which one implies the next.
| State | Evidence that supports it | Limit |
|---|---|---|
| Installed | A known executable, app bundle or install marker exists in the inspected location | A leftover config alone does not prove an executable is installed or runnable. The collector's presence: present can mean either configuration or installation evidence; inspect evidence_kinds |
| Configured | A recognized config or route declaration is readable; enrollment records describe configuration attempts | A file or enrollment acknowledgment does not prove login, effective policy, app consumption or a successful controlled action |
| Observed active | A hook, gateway, MCP or runtime-session event records activity on that route in a stated window | Inventory does not observe running processes, prompts or recent use. Offline usage and auth remain unknown; a runner heartbeat alone is not app activity |
| Governed | A particular action reaches a configured policy boundary and its decision is recorded; real-app acceptance verifies the tested behavior | A managed-looking MCP entry, runner governance label, enrollment or model-route setting does not verify all native tools, other connectors or alternate accounts |
The offline collector, route classifier and
control assertions keep those distinctions explicit. Offline
control axes are model_route, native_actions and managed_mcp; they distinguish
adapter support from recognized local configuration. Verification stays
unverified with evidence_kind: unavailable, even if an earlier enrollment
probe passed. A route classified as managed is a recognized configuration
shape, not an authenticated account binding or proof that a request succeeded.
Other sources answer different questions:
| Signal | Supported conclusion | Does not prove |
|---|---|---|
| Runner harness inventory | Bounded installed/configured metadata from a connected runner | Employee use, successful policy enforcement or all of the owner's devices |
| Gateway, native approval hook or MCP policy event | Activity and decisions for requests reaching that configured surface | Off-route API calls, browser activity or native tools outside that hook |
| Expected-device roster joined to reports | Reporting history for a separately declared device population, when the optional fleet service/capability is available | Discovery of unknown devices, hardware attestation or malicious intent behind a missing report |
| Imported provider usage or billing reconciliation | Vendor-reported activity/spend for the imported account and period | A device-level event, gateway-metered tokens or prevention of those calls |
| Separately supplied network logs | Destinations and times within that logging system's scope | Prompt content, the actor behind a shared address or endpoint enforcement by this collector |
The fleet architecture separates roster, receipt, observation and credential records. Fresh receipt must not be relabeled verified compliance. An empty scan applies only to its declared scope; incomplete, failed or stale collection does not establish absence. Preserve each source's window and conflicts. No report means unknown, not compliant or malicious.
Platform scope¶
The table describes current source paths, not a blanket app support policy. macOS and Windows have native collector implementations; Linux is best effort. All rows require readable known locations and the correct execution identity.
| OS | Installed signal | Configured signal | Observed active | Governed signal / boundary |
|---|---|---|---|---|
| macOS | Current-user known executable/bundle/marker probes; all-profile scans leave runtime installation unknown | Current-user known configs; elevated local /Users profile enumeration |
Unknown from inventory; requires separate routed events | Offline verification unverified. Claude Code hook overlay can be exported for macOS; app-user execution and effective managed source need acceptance |
| Windows | Current-user known executable/marker probes; all-profile scans leave runtime installation unknown | Current-user known configs; elevated local registry ProfileList enumeration |
Unknown from inventory; requires separate routed events | Offline verification unverified. Windows Code overlay can be exported; SYSTEM is not the app user, and live PowerShell/app/MDM acceptance remains unverified |
| Linux, including a CLI running inside WSL | Current-user known executable/marker probes; all-profile scans leave runtime installation unknown | Current-user known configs; elevated /home and /root enumeration |
Unknown from inventory; requires separate routed events | Best effort. Linux Code overlay can be exported; distribution, permissions and session environment need acceptance. A WSL scan does not establish coverage of Windows host profiles or other distributions |
Sources: platform profile paths and limits, Unix profile enumeration, Windows profile enumeration and the recipe prerequisite matrix. The repository already has a three-OS discovery CI matrix. Expanded native-path assertions and isolated fixture runs are planned under #1342, with passing supplemental results on the still-unmerged PR #1995 at review time. Those results do not certify a vendor-tenant rollout.
Collection limits¶
- All-profile collection requires root on macOS/Linux or administrator on
Windows, and explicit
--all-users --inventoryor--all-users --report. It uses known local profile stores, not directory-service or remote enumeration. - All-profile scans inspect at most 64 profiles and bound configuration reads to 4 MiB per file. Reports contain at most 100 application contexts. These are collection/report limits, not a promise to cover every installed app.
- Profile scans reject unsafe paths, symlinks, hard-linked configurations and
special files. They never execute a discovered app or read its credential
store. They do not search each user's runtime
PATH; runtime installation remains unknown. Failures and limits preserve incomplete scope. - Portable installs, nonstandard paths, remote homes, containers, other VMs, unmanaged/second devices, personal browser accounts and browser AI content are outside the inspected scope unless separately evidenced. The collector does not perform inline network inspection.
- The managed Code overlay supplies only
PreToolUse. Desktop routing, model routing, direct MCP connectors and native action gates are separate surfaces. Unattended enrollment covers selected Code, Codex and Cursor MCP configs; it is not authorization to enroll every discovered profile.
See all-user reporting
for failure states. The report builder sends only
present profile/app contexts, with installed: unknown, configured: true
when a config parsed successfully (otherwise unknown), and enrolled: false.
It does not emit active-use or runtime-verification evidence. An empty
candidate list without completeness evidence cannot establish a clean scan.
Planned work and separate controls¶
App-level control verification remains planned under #1182; a local configuration assertion is not its result. A Preloop-owned resident collector is a gated plan, #1348, not a property of the scheduled CLI collector. Broader signatures are tracked in #1341.
Existing Copilot imports supply vendor-reported data. The additional activity/inventory panels and import-completeness changes in PR #1996 are planned, not merged behavior at this review. See the public endpoint program tracker #1353 for reconciliation and egress-import plans; they are not collector signals. No device or runtime enforcement should be inferred from a vendor usage total.
Endpoint management, SaaS administration, identity/access controls, billing reconciliation and network security tools provide complementary evidence. Their coverage, permissions and retention must be assessed separately. This guide does not claim a native integration or inline inspection merely because one of those categories can help close a gap.
Threat model¶
The trust boundary is the selected collector scope and the requests that actually reach a policy boundary. Inventory is a client assertion, not device attestation. Use the controls below only within their stated authority.
| Threat or blind spot | Detecting or preventing control in scope | Residual risk / separate control to validate |
|---|---|---|
| Local administrator bypass | Recognized config/route probes can expose differences on a later scan; device-manager file permissions can restrict standard-user writes | Admin can replace software, disable hooks, suppress reports or choose another route. No local-admin resistance or signed hardware attestation is established. Validate independently administered resource/provider controls |
| Personal account or alternate client | Hook/gateway/MCP policy evaluates requests reaching its configured surface; imported usage covers the connected vendor account | Personal/browser accounts and off-route calls can be outside both sources. Validate organization identity and resource access policies separately |
| API-to-API usage or remote cloud agent | Gateway events cover routed API calls; separately imported provider records may expose activity/spend | A direct provider/service call bypasses endpoint discovery and gateway policy. Scope service credentials and egress controls independently |
| Browser AI | No browser-content detection by this collector | Browser prompts, uploads and personal sessions remain unknown. Any browser, vendor or network control requires its own authorization and evidence |
| Unmanaged or second device | An optional expected-device roster can expose missing reports for devices already known to it | Unknown devices cannot be inferred from silence. Validate enrollment and resource access requirements separately |
| Stolen collector key | Exactly report_discovery limits access to salt/report APIs, subject to permission checks; keys can be revoked independently |
A key can submit misleading metadata and obtain the account hash key. A generic report-only key is not bound to a device fingerprint by its scope alone. Use separately provisioned device restrictions where available; hashes and receipt are not attestation |
| Direct MCP connector | Recognized route counts expose configured direct remote/stdio entries; policy controls only calls through the managed entry | Other connector credentials may bypass that entry. Restrict them at the resource and verify a harmless denied action has no downstream effect |
| Offline or suppressed collector | Collection errors and receipt windows expose gaps for declared sources | No report does not mean no activity or deliberate evasion; distinguish outages, scheduling gaps and missing user sessions |
| Higher-priority policy, credential change or app upgrade | Reinspect configuration and repeat real-app acceptance for the changed environment | Delivered JSON may be ignored or superseded. Previous runtime results cannot certify a new configuration or version |
The key boundary is implemented in scope checks and
report authorization. Device-restricted credentials with
device_report context fail with 503 when their authorization service is
absent. This does not add a fingerprint restriction to a generic scoped key.
Use distinct collector and app/runtime credentials; neither belongs in a
static package with account-wide administrator authority.
Data minimization and retention¶
These are collector-specific DPIA inputs, not a completed DPIA or a retention promise for all Preloop data. The purpose is to identify known app/config contexts and reporting gaps for an authorized device population. Pseudonymous references, software choices and collection times can still relate to people, especially when joined to an administrative roster or enrollment.
Fields and data paths¶
| Path | Emitted fields and behavior | Important unknowns / exclusions |
|---|---|---|
Offline discover --inventory |
schema, observed_at, collector version/detector version, scope, completeness, probe app_id/kind/status/error code, apps with presence/evidence kinds, usage/auth, MCP config/server counts, route category counts/completeness, control support/configuration/verification fields |
No network/authentication by this mode; usage/auth remain unknown. Config names and values are inspected locally for classification but not copied into its output |
Offline --all-users --inventory |
The same inventory plus hashed profile_ref on apps/probes and per-profile completeness/error codes |
Fresh random hash key each run; no machine-identity lookup. Output can still be retained or uploaded by your scheduler |
Current-user discover --report |
workstation_fingerprint, OS family, optional valid CLI version; each candidate has agent_kind, config_path_hash, mcp_server_count, enrolled |
No collection-window/completeness envelope is emitted by this path. Enrollment observed by the CLI is not runtime governance |
--all-users --report |
The candidate fields plus v2 evidence: observation UUID, collector/detector versions, OS, source_environment: unknown, start/end times, scan_scope: [user_config], completeness/errors, profile refs/states, app kind/context/profile refs and installed/configured states |
No app versions, activity state or controls are emitted by this builder. All-user candidate enrolled: false means enrollment was not observed |
| Additional fields accepted by the API | Optional candidate agent_version; evidence v1/v2 can additionally carry app version, surface, observed_active and independent control assertions (supported, requested, applied, method) |
Accepted schema fields are not proof that the stock CLI collects them. There is no effective-policy or successful runtime-verification field. Evidence requires an installed discovery_evidence service; without it the report returns 503 |
The exact contracts are the offline DTOs, current-user builder, all-user builder, request schema and evidence schema. The latter bounds collection windows to one day, profiles to 64, applications to 100 and control assertions to seven per app. Unknown object fields are rejected. Agent-kind/version strings have restricted alphabets; they are not all enums. Validation constrains shape and size, not the truthfulness or sensitivity of an arbitrary client's schema-valid values.
The shipped serializers omit raw user names, hostnames, home/config paths, credentials, environment values, prompts, MCP names, URLs, commands and arguments from these report bodies. This is not a statement about gateway/session data, HTTP access logs, source IPs, webhook recipients or MDM logs. Assess those separately. Runner inventory is also a separate payload, including login state and restricted version/account host fields, not part of this narrow collector contract.
On receipt, candidate storage adds account/row identifiers,
first/last-seen times, status and any managed-agent link. Re-reporting updates
last_seen_at only; the other candidate details are not a current-state history.
Observation storage separately retains validated evidence,
authenticated source reference, observation UUID, end/receipt times and a
SHA-256 content digest for replay consistency. That digest is not an identity
hash or attestation. A new candidate also creates an
agent.discovered event with candidate identifiers, keyed refs,
kind/version, MCP count, enrollment flag, OS, status and first-seen time; configured
webhook destinations are additional recipients with their own retention.
Hashing and identity¶
The server issues a random 32-byte, hex-encoded account salt. The CLI uses that string as the HMAC-SHA256 key, producing lowercase 64-character hex references. The salt is available to authorized reporters and is not rotated automatically (salt model).
| Reference | Input under that key |
|---|---|
| Workstation | preloop-workstation:v1: plus the trimmed OS machine ID or explicit --device-ref |
| Current-user config | preloop-config-path:v1: plus the cleaned path, with the home prefix replaced by ~ and separators normalized |
| Reported profile | preloop-profile:v1: plus workstation fingerprint, : and cleaned home path (or local reference for an unreadable profile) |
| Reported profile/app context | preloop-profile-app:v2: plus profile reference, : and fixed app ID |
Machine identity uses macOS IOPlatformUUID, Windows MachineGuid, or Linux
/etc/machine-id with /var/lib/dbus/machine-id fallback
(Unix, Windows). Current-user
reporting can persist a random fallback in the CLI config directory. All-user
reporting only reuses an existing fallback or requires --device-ref; it does
not create one. Offline all-profile references instead use a new random
32-byte hex key and the context offline on each run.
Different account keys produce different references, but this does not anonymize the data or prevent correlation through other identifiers. A holder of the salt can test candidate identifiers. The current-user config hash can repeat for the same home-relative path across machines; its workstation key provides the candidate context. Profile/app hashes use a different namespace from enrollment config hashes and do not automatically link to current-user enrollment. Neither an explicit device reference nor an OS identifier attests physical hardware or the reporting executable.
Retention and deletion boundaries¶
| Store | Shipped lifecycle | DPIA implication |
|---|---|---|
| Discovery candidates | Eligible for deletion when last_seen_at is older than 90 days, except onboarded candidates still linked to a managed agent |
Linked device records have no 90-day cap. Agent deletion clears the link; a stale candidate becomes eligible for a later purge, not necessarily immediate deletion |
| Source observations | Expire 90 days from server received_at; reads filter expired records and purge deletes them |
A later report does not refresh an old observation's retention window |
| Purge execution | Background sweeper runs every 24 hours in API/all service roles, including when the evidence plugin is disabled | Deletion depends on a running, successful sweeper. The cutoff is not a guarantee of physical deletion exactly at 90 days |
| Account discovery salt | Stored per account, no automatic rotation or age-based expiry | It is not purged with old candidates; account deletion has a database cascade |
| Local inventory output and fallback identity file | No age-based deletion in these collector paths or example scripts | Configure scheduler/log rotation and endpoint decommissioning separately |
| Roster mappings, runner inventory, event/outbox deliveries, gateway/session/audit stores, backups and exports | Separate lifecycles, outside this discovery purge | No deployment-specific maximum or backup erasure deadline is established here; obtain and test each applicable policy |
Sources: candidate purge, observation purge,
sweeper, service roles and
salt/managed-agent foreign keys. The salt endpoint's
retention_days: 90 does not override the linked-candidate exception or govern
these other stores.
DPIA decisions still required¶
Before a rollout, record the controller/processor roles, purpose and lawful
basis, necessity of all-profile collection, affected people and notice, roles
allowed to view evidence, recipient systems, storage locations, transfers,
retention for each separate store and the deletion process. view_agents
permits candidate reads and manage_agents permits status changes; marking
ignored hides a candidate and is not erasure. Evidence/fleet service access
must be checked for the deployed edition and role configuration.
Repository source cannot establish your organization's lawful basis, vendor retention, data residency, backup expiration, scheduler output retention or actual purge health. Record these as unresolved until supplied by the operator. Use synthetic data to inspect stored reports and downstream events, verify expiry and linked-agent deletion, and exercise access/revocation before relying on the deployment's privacy statement.
Pilot evidence to retain¶
Record CLI/server/app versions, effective managed source/hash, collection scope and freshness, one permitted read, one denied action with no downstream effect, one approval, credential revocation, outage, alternate route and rollback. Keep synthetic service data and redact credentials/content from screenshots. Separate delivery receipts, inventory and actual app actions in the result. Browser use and unmanaged devices stay explicit exclusions until independently covered. Follow the recipe acceptance checklist without treating fixture checks as vendor-tenant acceptance.