Security
This page describes what a self-hosted Watcher deployment collects, stores, and sends. Use it for security and compliance review.
What is collected
The Watcher client records coding-agent sessions. Each recorded session contains:
- The full conversation. This includes user prompts, assistant replies, the system prompt, and tool calls with their arguments and results. Tool results include whatever the agent read or produced. Transcripts can contain source code, file contents, and any secrets the agent encountered.
- The agent that produced the session (Claude Code or Codex), timestamps, and the developer's identity. In proxy mode, this is the identity header your proxy injects. In SSO mode, it comes from the SSO sign-in. See authentication mode.
- Blocking review decisions. This includes the tool context, score, and outcome for each reviewed call.
- Grades. These are the scores, explanations, and summary text produced by graders.
Watcher does not record the screen, keystrokes, terminal contents, or terminal identifiers such as the tty. Watcher records what the agent did, not what the developer did outside the agent.
Where it is stored
The durable copy lives in your deployment's Postgres database. This is either the bundled Docker Postgres or the managed Postgres you configured. Operations covers server backups. The state on each developer's machine is covered on the client page.
Which LLM providers are called
All LLM grading runs from your Watcher deployment, never from developer machines. Blocking review's triage and gateway stages send the transcript excerpt from the client to your deployment. Your deployment calls the grading model. Trailing review grades server-side the same way.
The API makes provider calls. The grading scheduler triggers server-side grading and needs database access. It does not hold provider keys.
The scheduler does hold database credentials. Each pass connects to Postgres to
list the organizations to grade. Then it calls the API for each one. It reads no
session content. Include it when you inventory which containers receive database
credentials. Allow it a route to Postgres when you restrict network or database
access. If a policy blocks that connection, the scheduler logs
async grading sweep failed. It retries on the next interval. Trailing review
stays stopped until you restore access.
The xylon-retention-cleanup container also holds database credentials. It runs
the xylon-api image with a dedicated entrypoint. It contacts no LLM provider.
It connects to Postgres to delete expired unlinked grader inputs. It also
enforces any enabled organization session-content retention policy. That policy
is disabled by default. Organization administrators configure it through the
Analyzer's
Data Retention controls
or the organization settings API. If the worker cannot reach Postgres, it logs
the failed sweep and retries on the next interval.
The default managed monitors use three providers across four monitors:
- Triage uses OpenAI's
gpt-5.6-luna - Gateway uses Anthropic's
claude-opus-4-7 - Deep Review uses Anthropic's
claude-sonnet-5 - Session summaries use Google's
gemini-2.5-flash
Obtain access to these exact models before setup. They are Watcher's recommended and tested configuration.
Grading calls send session content to the provider. This includes prompts, agent
output, and tool results. You supply the provider API keys. Provider choice,
accounts, and data-processing terms are yours. You can override monitor models
in server-config.yaml when your deployment uses a different provider set.
Alternative models are not Watcher's recommended, currently tested
configuration. They may impede monitor performance. A missing key or unavailable
model makes the affected grading path fail. Watcher does not select another
model automatically.
Self-hosted Watcher does not phone home
The stack sends no usage analytics, error reporting, or other telemetry to Apollo. It makes these outbound connections when configured:
- LLM APIs: grading sends session content to the configured model providers.
- WorkOS: in SSO mode, the API uses WorkOS for sign-in, token management, and organization membership. Proxy mode does not use it.
- Slack: if an administrator configures a webhook, notifications send the
grader's summary, scores, and an Analyzer link by default. Blocking-review
alerts can also contain tool arguments and paths. Choose Findings and aggregates only in
organization settings to omit
session summaries and raw tool content; findings may still contain transcript
excerpts. Recipients' Watcher content permissions are not checked; control
access through Slack channel membership. Watcher replaces credentials in
common formats with
[REDACTED]before sending, but a secret in another format can still appear. To keep this traffic inside your network boundary, set theSLACK_WEBHOOK_URLenvironment variable on the API to an internal relay that forwards to Slack: it overrides the webhook URL saved in organization settings (see Organization settings). A URL saved in organization settings must usehttps://. TheSLACK_WEBHOOK_URLvariable acceptshttp://as well, for a relay on a trusted network that does not terminate TLS. Overhttp://the notification and the webhook URL itself are sent in cleartext and no certificate is verified, and the URL's path is the credential that lets anyone holding it post to the channel, so usehttp://only on a network you trust to carry both. Anhttps://destination has its certificate verified on every send, so an internal relay reached overhttps://needs a certificate that chains to a publicly trusted root (the trust requirement is spelled out on the linked page).
Installation and upgrades pull server images from a container registry. They also pull client files from a public release or internal mirror. These requests contain no session data. See Supply chain security for internal mirror options.
The Watcher client sends session data only to the backend recorded during installation. The deployment example shows these connections.
Admin access auditing
Admins can see every user's sessions through the Analyzer. The deployment keeps the reciprocal record. It maintains an admin audit log, on by default. The log appends one JSON line for each of these actions:
- Reads of another user's session data
- Session list or search requests
- User-directory reads
- Organization-level settings writes
- Session-content scrubs (always recorded)
Administrator actions carry the actor's email. Automatic retention uses
retention-policy as the actor and system as the authentication method.
Records carry resource identifiers, never session content. The logs stay in a
Docker volume for Compose. For Kubernetes, they stay in
persistent volume claims. They
contain personal data and are not collected by the support bundle.
Operations covers enabling, reviewing, and
pruning it.
Retention and deletion
By default, Watcher retains session content indefinitely. An administrator can
remove the stored content of one session. Use
DELETE /api/v1/sessions/{session_id}/content. This operation removes the
session's messages, tool-call records, and linked blocking-review decisions and
grades from the active Postgres database. The retained records described below
can still contain session-derived prose.
The server also supports an organization-level inactive-session retention
policy. It is disabled by default. Session content is retained indefinitely.
Organization administrators can enable it from the Analyzer's
Data Retention controls
or with PATCH /api/v1/organization/settings. The configured inactivity period
must be between 7 and 365 days. Setting it to null disables the policy. The
always-on retention worker removes content from sessions whose latest recorded
activity is at least the configured number of days old. The worker runs
immediately at startup and every 24 hours thereafter.
The scrub retains trajectory grades. This includes their explanations and summaries. It also retains notification titles and bodies, in their original form rather than the redacted form sent to Slack. These can contain sensitive excerpts from the session. Scrubbing does not redact them.
The scrub also retains the session record. This includes its historical activity dates, message count, and content-free activity history. For each tool call, it retains one content-free fact. This fact includes:
- The tool-call ID
- When the call happened
- What Watcher decided (allowed, denied, or escalated to a person, and under which enforcement mode)
- The numeric scores that decision was based on
These scores are recorded on the fact as the call is decided. They outlive the grades they came from. The fact never carries the tool's name, its arguments, or any prose. These facts keep tool-call counts, filters, and severity accurate for a scrubbed session in the Analyzer. A later full upload of the same session can restore its messages and reconnect retained trajectory grades. Scrubbing does not prevent re-upload.
Sessions created after distinct tool invocations are enabled work like this. Restoring the same conversation with the same tool-call IDs reconnects retained grades to the corresponding messages. This works even when branches are uploaded in a different order. Missing or reused IDs cannot distinguish otherwise identical invocations.
Sessions created before this setting was enabled work differently. They can treat equal-content tool calls at the same conversation position as the same message. This happens even when their IDs differ. Restoring such content after a scrub can reconnect a grade from an earlier invocation. That association does not establish that the restored invocation was graded. Upgrading or enabling the setting does not change these existing sessions. It does not repair earlier conflations.
A blocking-review grade whose corresponding decision has not yet arrived is removed by the scrub. This happens when the grade recorded which session it was graded for. Current Watcher clients write grades this way. Unlinked grades without that session reference cannot be removed by this session-specific operation. These come from older clients or were graded before the session's first upload. Watcher's always-on retention worker makes these unlinked grader inputs eligible for deletion after eight days. It deletes them on its next sweep. Linked grades and session content outside an enabled policy remain until an administrator scrubs the session. Scrubbing and the retention worker do not alter source files, client-local state, external copies, or existing backups.
Saved managed client settings documents are also retained. They are kept as an append-only version history.
Retention beyond these session-content and orphan-grade policies is yours to implement. Use standard database tooling. Backups remain under your control. See Operations.
On developer machines, watcher uninstall removes the hooks and the binary. It
intentionally keeps the local state under ~/.apollo_monitor/. It prints a
reminder to remove it manually. Uninstalling a client never touches the data
already uploaded to the deployment.
Client credential helpers
An external credential helper is trusted local code that runs with the developer account's privileges. Watcher validates its JSON output, extracts a bearer token, and limits invocation time and output. The gateway must validate token identity, audience, expiry, and authenticity before injecting identity or role headers. Block direct backend access and strip client-supplied versions of those headers. Helper output itself grants no authority, and owner-only state does not protect against unrestricted processes running as the same user.