Privacy

Agent Relay is local-first. Every byte written by the always-on layer lands in ~/.relay/ on your machine. Nothing uploaded unless you explicitly opt into the proxy mode or telemetry — and even then, the defaults stay conservative.

What gets captured

AdapterCaptures
Claude Code hookTool name on each invocation, stop reasons, notification text.
PTY wrapperProcess lifecycle + line-level rate-limit detection from stdout. The agent's full stdout is mirrored to your terminal, not copied to disk.
VS Code extensionFile-edit events (path + hunk count, not content). Session lifecycle. Manual handoff triggers.
Warp MCPWhatever the Warp agent decides to call relay_log_event with — under the agent's control.
Proxy mode (opt-in)LLM API response headers (rate-limit counters, retry-after). Request/response bodies are seen in flight but never written to disk.

What gets redacted

By default (privacy.redact_secrets = true), every adapter applies the same redaction rules before writing:

  • Object keys matching (secret|key|token|password|credential|bearer|api[_-]?key) → value replaced with [redacted].
  • Inline values matching well-known token formats → replaced inline:
    • sk-… (Anthropic / OpenAI)
    • ghp_… (GitHub personal access tokens)
    • xox[abp]-… (Slack)
  • Tool argument values are not captured by default (privacy.capture_tool_args = false) — we keep the structure ("agent called Edit with these arg names") but drop the values. Flip the flag if you want lossless capture and trust your redaction rules.

Same rules apply across the Python daemon, the VS Code extension, and the Warp MCP server. Source: src/agent_relay/daemon/redact.py and extensions/vscode/src/redact.ts.

Where it lives on disk

~/.relay/
├── VERSION                       # layout version
├── config.toml                   # your settings
├── sessions/                     # per-session manifests
├── events/<session>.jsonl        # append-only event log
├── snapshots/<id>.md             # handoff primers
├── logs/daemon.log               # daemon stdout/stderr
├── relay.sock                    # unix socket (POSIX)
└── telemetry-id                  # random UUID, only present if you opt in

To inspect everything captured for a session:

terminal
cat ~/.relay/events/<session>.jsonl       # raw events
relay snapshots                            # the resulting primers
relay daemon status                        # in-memory view

To wipe everything captured:

terminal
relay uninstall                            # remove adapters + launch agent
rm -rf ~/.relay                            # delete the data

Telemetry

Off by default. Toggle on in ~/.relay/config.toml:

toml
[telemetry]
enabled = true
endpoint = "https://telemetry.agent-relay.dev/v1/ingest"

When enabled we send aggregate-only counters: which adapters are in use, handoff success rate, the daemon version. No event content, no file paths, no environment variables, no per-session anything. The install id is a random UUID stored at ~/.relay/telemetry-id; deleting it rotates the id.

What gets sent (full schema):

json
{
  "schema": "agent-relay.telemetry/v1",
  "event": "handoff.fired",
  "install_id": "01HRZ…",
  "sdk_version": "0.1.0",
  "properties": {  aggregate counters }
}

The full source is at src/agent_relay/daemon/telemetry.py. Sends are background-threaded with a 3-second timeout and silently dropped on failure — telemetry can never block the daemon.

What we never store

  • Your source code.
  • LLM prompts or responses.
  • Environment variables.
  • .env file contents.
  • Tool argument values (with the default config).
  • Anything resembling an API key — even when the proxy is on.

Reporting concerns

If you find a redaction miss or a place where data leaves the machine unexpectedly, please open an issue at github.com/bethvourc/agent--relay. This is the area we treat as a hair-trigger bug class.