04Docs

Observe & PII

Observe watches your service’s egress and reports metadata, not payloads. Each call leaves as its shape: host, templated path, method, status, timing, and the actor who made it. Values never leave your process, and the PII denylist is compiled into the sensor so no policy can reach past it.

What the sensor captures

One event per outbound call, metadata only. Ids in the path are templated inside your process before the event is built: /v1/customers/cus_9f2 becomes /v1/customers/{id}. UUIDs, numeric ids, hashes, and vendor ids all collapse to {id}.

FieldWhat it holds
hostDestination host.
pathTemplated path. Ids collapse to {id} before the event is built.
method, statusHTTP method and response status.
durationTime to response, in milliseconds.
sizesRequest and response byte counts.
actorThe attributed actor and kind. An opaque internal id, never an email.
query namesQuery parameter names only, capped at 64. Values are never read.

Never on the wire

These never leave your process, on any route:

  • Request and response bodies. Unrecognized routes contribute top-level field names and types only, on the first sighting of a shape.
  • Query-string values. Names only, on shape samples.
  • Header values. Headers are scanned only to strip X-Bounded-* from egress.
  • Prompts, completions, and messages of LLM calls. They are in no safe-field list.

Authorization is never read. The wire envelope has no body slot at all.

Shape learning

The first time the sensor sees a new route it emits one shape event: the top-level field names and their JSON types, nothing else. Fields with PII-shaped names are dropped before the shape is built, so even their names never appear. Shape events are deduped in memory and rate-capped.

shape event (names and types only)
{
  "class": "shape",
  "host": "api.internal",
  "path": "/v1/customers/{id}",
  "fields": {
    "amount_cents": "number",
    "currency": "string",
    "created": "number"
  }
}

Stories and boundary suggestions are built from shapes and safe fields, not from payloads.

The layered posture

Capture is default-deny. Three layers decide what can be recorded, and two of them run again on the server, independent of the sensor.

LayerWhat it holds
AllowlistOnly manifest-listed safe fields are read. Primitives only, strings capped in length.
DenylistAbout 45 compiled patterns (email, phone, ssn, tokens, cards, addresses, names) are refused in the sensor and re-checked server-side. A manifest that lists customer_email still cannot capture it.
Value scrubberAt ingest, values matching email, JWT, or Luhn-valid card patterns become [REDACTED:...] and bump a scrubbed:true counter you can see.

Progressive disclosure

Capture widens by policy, not by code. Adding a named safe field to the manifest is an explicit, versioned change: every event carries the policyVersion it was captured under, so you can see when capture changed and what it covered.

The denylist is not one of those levers. It is compiled into the sensor and cannot be overridden by any policy or manifest.