Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"group": "Tracing",
"pages": [
"tracing/introduction",
"tracing/pii-redaction",
{
"group": "SDKs",
"pages": [
Expand Down Expand Up @@ -128,6 +129,11 @@
]
},
"contextual": {
"options": ["copy", "view", "chatgpt", "claude"]
"options": [
"copy",
"view",
"chatgpt",
"claude"
]
}
}
6 changes: 6 additions & 0 deletions tracing/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,9 @@ Create an API key from [Settings → API Keys](https://app.zeroeval.com/settings
skill](/integrations/skills) can handle SDK setup, first trace, and prompt
migration for you.
</Tip>

<Note>
Need source-side PII redaction before span data is buffered, logged, or sent?
ZeroEval supports opt-in redaction in both SDKs. See [PII
redaction](/tracing/pii-redaction).
</Note>
183 changes: 183 additions & 0 deletions tracing/pii-redaction.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
---
title: "PII redaction"
description: "Opt-in SDK-side redaction for tracing data in the Python and TypeScript SDKs"
---

ZeroEval supports opt-in source-side PII redaction in the Python and TypeScript
SDKs. When enabled, sensitive values are redacted before spans are buffered,
logged, or sent by the SDK.

<Note>
This is an SDK-side feature. ZeroEval does not rely on backend-side redaction
for this behavior.
</Note>

## Enable redaction

<Tabs>
<Tab title="Python">
```python
import zeroeval as ze

ze.init(
api_key="YOUR_API_KEY",
redaction={"enabled": True},
)
```
</Tab>
<Tab title="TypeScript">
```typescript
import * as ze from "zeroeval";

ze.init({
apiKey: "YOUR_API_KEY",
redaction: { enabled: true },
});
```
</Tab>
</Tabs>

You can also enable the default redaction toggle with:

```bash
export ZEROEVAL_REDACT_PII=true
```

Python uses snake_case nested keys such as `redact_inputs`,
`redact_session_names`, `sensitive_keys`, and `custom_patterns`. TypeScript uses
the camelCase equivalents `redactInputs`, `redactSessionNames`,
`sensitiveKeys`, and `customPatterns`.

## What gets redacted

By default, the SDKs redact sensitive values found in:

- inputs and outputs
- attributes
- error messages and stacks
- session names
- tag values

Built-in detectors cover:

- email addresses
- phone numbers
- SSN-style identifiers
- PAN / credit card numbers
- bearer tokens, JWTs, and common API key formats
- cookie and authorization header values
- IP addresses

Key-based detection also force-redacts common sensitive fields such as
`email`, `phone`, `password`, `token`, `authorization`, `cookie`, and
`api_key` / `apiKey`.

## What stays intact

Redaction is meant to preserve trace structure. The SDKs keep:

- span names
- trace IDs and span IDs
- timing and status fields
- token counts, model/provider metadata, and cost metadata unless the value
itself is sensitive

## Placeholder behavior

Sensitive values are replaced with stable placeholders inside a single trace:

```text
alice@example.com -> [REDACTED_EMAIL_A]
bob@example.com -> [REDACTED_EMAIL_B]
alice@example.com -> [REDACTED_EMAIL_A]
```

- the same normalized sensitive value in one trace gets the same placeholder
- different values in the same trace get different placeholders
- placeholder assignment resets per trace
- matching is exact after normalization only
- this is not fuzzy identity resolution

That means repeated references across parent and child spans stay joinable
without preserving the original value.

## Normalization and limitations

| Type | Normalization used for placeholder reuse |
| --- | --- |
| Email | Trim + lowercase |
| Phone | Digits only |
| SSN / PAN | Digits only |
| IP | Canonical or lowercase string as implemented by each SDK |
| Secrets | Exact trimmed string |

Important limitations:

- there is no reversible backend token vault
- there is no de-anonymization support
- there is no fuzzy entity resolution across semantically related strings
- bypassing SDK capture paths bypasses this protection

## Examples

The SDK repositories include runnable examples that match the implemented
behavior:

<Tabs>
<Tab title="Python">
From `zeroeval-sdk/examples/pii_redaction.py`:

```python
import zeroeval as ze

ze.init(
api_key="sk_ze_demo_local",
redaction={"enabled": True},
)

with ze.span(
name="pii-redaction-demo",
session={"id": "alice@example.com", "name": "Alice alice@example.com"},
tags={"customer_email": "alice@example.com"},
) as span:
span.set_io(
input_data={"email": "alice@example.com", "phone": "+1 (415) 555-1234"},
output_data={"result": "Reach alice@example.com"},
)
span.set_error(
code="ValueError",
message="Failed for alice@example.com with Bearer secret-demo-token",
)
```
</Tab>
<Tab title="TypeScript">
From `zeroeval-ts/examples/10-pii-redaction.ts`:

```typescript
import * as ze from "zeroeval";

ze.init({
apiKey: "demo-api-key",
redaction: { enabled: true },
});

const span = ze.tracer.startSpan("demo.pii_redaction", {
sessionId: "alice@example.com",
sessionName: "Alice Example <alice@example.com>",
tags: { customer_email: "alice@example.com" },
});

span.setIO(
{
email: "alice@example.com",
phone: "+1 (415) 555-1212",
apiKey: "sk-live-abcdef1234567890",
Comment thread
sebastiancrossa marked this conversation as resolved.
},
"Send follow-up to alice@example.com and bob@example.com"
);
```
</Tab>
</Tabs>

In both SDKs, repeated exact values such as the same email address will reuse
the same placeholder within the trace.
30 changes: 27 additions & 3 deletions tracing/sdks/python/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ def init(
api_url: str = None,
disabled_integrations: list[str] = None,
enabled_integrations: list[str] = None,
setup_otlp: bool = True,
setup_otlp: bool = False,
service_name: str = "zeroeval-app",
tags: dict[str, str] = None,
sampling_rate: float = None
sampling_rate: float = None,
redaction: dict[str, object] = None
) -> None
```

Expand All @@ -40,10 +41,11 @@ def init(
| `api_url` | `str` | `"https://api.zeroeval.com"` | API endpoint URL |
| `disabled_integrations` | `list[str]` | `None` | Integrations to disable (e.g. `["langchain"]`) |
| `enabled_integrations` | `list[str]` | `None` | Only enable these integrations |
| `setup_otlp` | `bool` | `True` | Configure OpenTelemetry OTLP export |
| `setup_otlp` | `bool` | `False` | Configure OpenTelemetry OTLP export |
| `service_name` | `str` | `"zeroeval-app"` | OTLP service name |
| `tags` | `dict[str, str]` | `None` | Global tags applied to all spans |
| `sampling_rate` | `float` | `None` | Sampling rate 0.0-1.0 (1.0 = sample all) |
| `redaction` | `dict[str, object]` | `None` | Source-side PII redaction settings |

**Example:**

Expand All @@ -54,10 +56,30 @@ ze.init(
api_key="your-api-key",
sampling_rate=0.1,
disabled_integrations=["langchain"],
redaction={"enabled": True},
debug=True
)
```

`redaction` uses snake_case keys in Python:

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `enabled` | `bool` | `False` | Turn source-side redaction on |
| `redact_inputs` | `bool` | `True` | Redact `input_data` |
| `redact_outputs` | `bool` | `True` | Redact `output_data` |
| `redact_attributes` | `bool` | `True` | Redact span attributes |
| `redact_errors` | `bool` | `True` | Redact error messages and stacks |
| `redact_session_names` | `bool` | `True` | Redact session names |
| `redact_tag_values` | `bool` | `True` | Redact span, trace, and session tag values |
| `sensitive_keys` | `list[str]` | built-in list | Force-redact matching keys such as `email`, `api_key`, `authorization`, and `cookie` |
| `custom_patterns` | `list[Pattern[str] \| str]` | `[]` | Additional regex patterns to redact |

<Tip>
See [PII redaction](/tracing/pii-redaction) for scope, placeholder behavior,
normalization rules, and examples.
</Tip>

## Decorators

### `@span`
Expand Down Expand Up @@ -895,6 +917,7 @@ Set before importing ZeroEval to configure default behavior.
| `ZEROEVAL_SAMPLING_RATE` | float | `"1.0"` | Sampling rate (0.0-1.0) |
| `ZEROEVAL_DISABLED_INTEGRATIONS` | string | `""` | Comma-separated integrations to disable |
| `ZEROEVAL_DEBUG` | boolean | `"false"` | Enable debug logging |
| `ZEROEVAL_REDACT_PII` | boolean | `"false"` | Enable SDK-side PII redaction |

```bash
export ZEROEVAL_API_KEY="ze_1234567890abcdef"
Expand All @@ -919,6 +942,7 @@ ze.tracer.configure(
flush_interval=0.5,
max_spans=100,
sampling_rate=0.05,
redaction={"enabled": True},
integrations={"openai": True, "langchain": False}
)
```
Expand Down
8 changes: 7 additions & 1 deletion tracing/sdks/python/setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ ze.init(api_key="YOUR_API_KEY")
`~/.config/zeroeval/config.json`
</Note>

<Tip>
Need source-side PII redaction? Pass `redaction={"enabled": True}` to
`ze.init(...)` or set `ZEROEVAL_REDACT_PII=true`. See [PII
redaction](/tracing/pii-redaction).
</Tip>

## Patterns

### Decorators
Expand Down Expand Up @@ -301,4 +307,4 @@ zeroeval setup

# Run scripts with automatic tracing
zeroeval run my_script.py
```
```
24 changes: 23 additions & 1 deletion tracing/sdks/typescript/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ function init(opts?: InitOptions): void;
| -------------------- | ------------------------- | -------------------------- | --------------------------------------- |
| `apiKey` | `string` | `ZEROEVAL_API_KEY` env | Your ZeroEval API key |
| `apiUrl` | `string` | `https://api.zeroeval.com` | Custom API URL |
| `workspaceName` | `string` | `"Personal Organization"` | Workspace/organization name |
| `workspaceName` | `string` | `"Personal Workspace"` | Workspace/organization name |
| `flushInterval` | `number` | `10` | Interval in seconds to flush spans |
| `maxSpans` | `number` | `100` | Maximum spans to buffer before flushing |
| `collectCodeDetails` | `boolean` | `true` | Capture source code context |
| `integrations` | `Record<string, boolean>` | — | Enable/disable specific integrations |
| `debug` | `boolean` | `false` | Enable debug logging |
| `redaction` | `Partial<RedactionConfig>` | — | Source-side PII redaction settings |

#### Example

Expand All @@ -39,10 +40,30 @@ import * as ze from "zeroeval";

ze.init({
apiKey: "your-api-key",
redaction: { enabled: true },
debug: true,
});
```

`redaction` uses camelCase keys in TypeScript:

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `enabled` | `boolean` | `false` | Turn source-side redaction on |
| `redactInputs` | `boolean` | `true` | Redact `input_data` |
| `redactOutputs` | `boolean` | `true` | Redact `output_data` |
| `redactAttributes` | `boolean` | `true` | Redact span attributes |
| `redactErrors` | `boolean` | `true` | Redact error messages and stacks |
| `redactSessionNames` | `boolean` | `true` | Redact session names |
| `redactTagValues` | `boolean` | `true` | Redact span, trace, and session tag values |
| `sensitiveKeys` | `string[]` | built-in list | Force-redact matching keys such as `email`, `apiKey`, `authorization`, and `cookie` |
| `customPatterns` | `Array<RegExp \| string>` | `[]` | Additional patterns to redact |

<Tip>
See [PII redaction](/tracing/pii-redaction) for scope, placeholder behavior,
normalization rules, and examples.
</Tip>

---

## Wrapper Functions
Expand Down Expand Up @@ -408,6 +429,7 @@ Set before importing ZeroEval to configure default behavior.
| `ZEROEVAL_SAMPLING_RATE` | float | `"1.0"` | Sampling rate (0.0-1.0) |
| `ZEROEVAL_DISABLED_INTEGRATIONS` | string | `""` | Comma-separated integrations to disable |
| `ZEROEVAL_DEBUG` | boolean | `"false"` | Enable debug logging |
| `ZEROEVAL_REDACT_PII` | boolean | `"false"` | Enable SDK-side PII redaction |

```bash
export ZEROEVAL_API_KEY="ze_1234567890abcdef"
Expand Down
7 changes: 7 additions & 0 deletions tracing/sdks/typescript/setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ ze.init({
});
```

<Tip>
Need source-side PII redaction? Pass `redaction: { enabled: true }` to
`init(...)` or set `ZEROEVAL_REDACT_PII=true`. See [PII
redaction](/tracing/pii-redaction).
</Tip>

## Patterns

The SDK offers two ways to add tracing to your TypeScript/JavaScript code:
Expand Down Expand Up @@ -251,6 +257,7 @@ ze.init({
maxSpans: 200, // Buffer up to 200 spans
collectCodeDetails: true, // Capture source code context
debug: false, // Enable debug logging
redaction: { enabled: true },
integrations: {
openai: true, // Enable OpenAI integration
vercelAI: true, // Enable Vercel AI SDK integration
Expand Down
Loading