> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abv.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Masking of Sensitive LLM Data

Masking is a feature that allows precise control over the tracing data sent to the ABV server. With custom masking functions, you can control and sanitize the data that gets traced and sent to the server. Whether it's for **compliance reasons** or to protect **user privacy**, masking sensitive data is a crucial step in responsible application development. It enables you to:

1. Redact sensitive information from trace or observation inputs and outputs.
2. Customize the content of events before transmission.
3. Implement fine-grained data filtering based on your specific requirements.

Learn more about ABV's data security and privacy measures concerning the stored data in our [security and compliance overview](/developer/references/security-compliance-overview).

# How it works

1. You define a custom masking function and pass it to the ABV client constructor.
2. All event inputs and outputs are processed through this function.
3. The masked data is then sent to the ABV server.

This approach ensures that you have complete control over the event input and output data traced by your application.

## Python SDK

Install package

```bash theme={null}
pip install abvdev
```

Define a masking function before the client initialization. The masking function will apply to all event inputs and outputs regardless of the ABV-maintained integration you are using.

```python theme={null}
def masking_function(data: any, **kwargs) -> any:
    """Function to mask sensitive data before sending to ABV."""
    if isinstance(data, str) and data.startswith("SECRET_"):
        return "REDACTED"

    # For more complex data structures
    elif isinstance(data, dict):
        return {k: masking_function(v) for k, v in data.items()}
    elif isinstance(data, list):
        return [masking_function(item) for item in data]

    return data
```

Apply the masking function when initializing the ABV client:

```python theme={null}
from abvdev import ABV

# Initialize with masking function
abv = ABV(mask=masking_function)
```

So it will look like below, with the decorator:

```python theme={null}
from abvdev import ABV, observe

abv = ABV(
    mask=masking_function,
    api_key="sk-abv-...", # your api key here
    host="https://app.abv.dev" # host="https://eu.app.abv.dev", for EU region
    )

@observe()
def my_function():
    # This data will be masked before being sent to ABV
    return "SECRET_DATA"

result = my_function()
print(result)  # Original: "SECRET_DATA"

# The trace output in ABV will have the output masked as "REDACTED"
```

Using context managers:

```python theme={null}
from abvdev import ABV

abv = ABV(
    mask=masking_function,
    api_key="sk-abv-...", # your api key here
    host="https://app.abv.dev" # host="https://eu.app.abv.dev", for EU region
    )

with abv.start_as_current_span(
    name="sensitive-operation",
    input="SECRET_INPUT_DATA"
) as span:
    # ... processing ...
    span.update(output="SECRET_OUTPUT_DATA")

# Both input and output will be masked as "REDACTED" in ABV
```

## JS/TS SDK

**Install packages**

```bash theme={null}
npm install @abvdev/tracing @abvdev/otel @opentelemetry/sdk-node dotenv
```

**Add credentials**

Add your ABV credentials to your environment variables. Make sure that you have a `.env` file in your project root and a package like dotenv to load the variables.

<CodeblockTabs>
  .env

  ```typescript theme={null}
  ABV_API_KEY = "sk-abv-..."
  ABV_BASE_URL = "https://app.abv.dev" # US region
  //ABV_BASE_URL = "https://eu.app.abv.dev" # EU region
  ```
</CodeblockTabs>

Create`instrumentation.ts` file and use `dotenv` package to load the variables.

Additional parameters are provided to get trace visible in the UI immediately.

<CodeblockTabs>
  instrumentation.ts

  ```typescript theme={null}
  import dotenv from "dotenv";
  dotenv.config();

  import { NodeSDK } from "@opentelemetry/sdk-node";
  import { ABVSpanProcessor } from "@abvdev/otel";

  const sdk = new NodeSDK({
    spanProcessors: [
      new ABVSpanProcessor({
        apiKey: process.env.ABV_API_KEY,
        baseUrl: process.env.ABV_BASE_URL,
        exportMode: "immediate",
        flushAt: 1,
        flushInterval: 1,
        additionalHeaders: {
          "Content-Type": "application/json",
          "Accept": "application/json"
        }
      })
    ],
  });

  sdk.start();
  ```
</CodeblockTabs>

Import the `instrumentation.ts` file at the top of your application.

**To prevent sensitive data from being sent to** ABV, you can provide a `mask` function to the `ABVSpanProcessor`. This function will be applied to the `input`, `output`, and `metadata `of every observation.

The function receives an object `{ data }`, where data is the stringified JSON of the attribute’s value. It should return the masked data.

<CodeblockTabs>
  instrumentation.ts

  ```typescript theme={null}
  import { NodeSDK } from "@opentelemetry/sdk-node";
  import { ABVSpanProcessor } from "@abvdev/otel";
  import dotenv from "dotenv";
  dotenv.config();

  const sdk = new NodeSDK({
    spanProcessors: [
      new ABVSpanProcessor({
        apiKey: process.env.ABV_API_KEY,
        baseUrl: process.env.ABV_BASE_URL,
        exportMode: "immediate",
        flushAt: 1,
        flushInterval: 1,
        additionalHeaders: {
          "Content-Type": "application/json",
          "Accept": "application/json"
        },
      
        mask: ({ data }) => {
          // A simple regex to mask credit card numbers
          const maskedData = data.replace(
            /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g,
            "***MASKED_CREDIT_CARD***"
          );
        
          return maskedData;
        },
      })
    ],
  });

  sdk.start();
  ```
</CodeblockTabs>

Simple example with masked data

```typescript theme={null}
import "./instrumentation_masked";
import { startActiveObservation } from "@abvdev/tracing";

async function main() {
  await startActiveObservation("my-masked-trace", async (span) => {
    span.update({
      input: "Hello, ABV! My card number is 5555-7777-3333-1111 ",
      output: "This is my first trace! And my card number is 1111-1111-1111-1111",
    });
  });
}

main();
```

Masked data will be shown in UI:

*Input: "Hello, ABV! My card number is \*\*\*MASKED\_CREDIT\_CARD\*\*\* "*

*Output: "This is my first trace! And my card number is \*\*\*MASKED\_CREDIT\_CARD\*\*\*"*

See [JS/TS SDK docs](/developer/sdks/js-ts/overview) for more details.

# Examples

Now, we'll show you examples how to use the masking feature. We'll use the ABV decorator for this, but you can also use the low-level SDK or the JS/TS SDK analogously.

## Example 1: Redacting Credit Card Numbers

In this example, we'll demonstrate how to redact credit card numbers from strings using a [regular expression](https://docs.python.org/3/library/re.html). This helps in complying with PCI DSS by ensuring that credit card numbers are not transmitted or stored improperly.

<hint type="info">
  ABVs masking feature allows you to define a custom masking function with parameters, which you then pass to the ABV client constructor. This function is applied to all event inputs and outputs, processing each piece of data to mask or redact sensitive information according to your specifications. By ensuring that all events are processed through your masking function before being sent, ABV guarantees that only the masked data is transmitted to the ABV server.
</hint>

**Steps:**

1. **Import necessary modules**.
2. **Define a masking function** that uses a regular expression to detect and replace credit card numbers.
3. **Configure the masking function** in ABV.
4. **Create a sample function** to simulate processing sensitive data.
5. **Observe the trace** to see the masked output.

```python theme={null}
import re
from abvdev import ABV, observe

# Step 2: Define the masking function
def masking_function(data, **kwargs):
    if isinstance(data, str):
        # Regular expression to match credit card numbers (Visa, MasterCard, AmEx, etc.)
        pattern = r'\b(?:\d[ -]*?){13,19}\b'
        data = re.sub(pattern, '[REDACTED CREDIT CARD]', data)
    return data

# Step 3: Configure the masking function
abv = ABV(
    mask=masking_function,
    api_key="sk-abv-...", # your api key here
    host="https://app.abv.dev" # host="https://eu.app.abv.dev", for EU region
    )

# Step 4: Create a sample function with sensitive data
@observe()
def process_payment():
    # Simulated sensitive data containing a credit card number
    transaction_info = "Customer paid with card number 4111 1111 1111 1111."
    return transaction_info

# Step 5: Observe the trace
result = process_payment()

print(result)
# Output: Customer paid with card number [REDACTED CREDIT CARD].

# Flush events in short-lived applications
abv.flush()
```

<img src="https://mintlify.s3.us-west-1.amazonaws.com/abv-2be93c70/images/I9Y7y-WoWDecPqSaNVjC__image.png" alt="" />

## Example 2: Using the `llm-guard` library

Use the `Anonymize` scanner from `llm-guard` to remove personal names and PII. See the [llm-guard documentation](https://llm-guard.com/) for more details.

```bash theme={null}
pip install llm-guard
```

```python theme={null}
from abvdev import ABV, observe
from llm_guard.vault import Vault
from llm_guard.input_scanners import Anonymize
from llm_guard.input_scanners.anonymize_helpers import BERT_LARGE_NER_CONF

vault = Vault()

def masking_function(data, **kwargs):
    if isinstance(data, str):
        scanner = Anonymize(vault, recognizer_conf=BERT_LARGE_NER_CONF, language="en")
        sanitized_data, _, _ = scanner.scan(data)
        return sanitized_data
    return data

abv = ABV(
    mask=masking_function,
    api_key="sk-abv-...",
    host="https://app.abv.dev"
)

@observe()
def generate_report():
    return "John Doe met with Jane Smith to discuss the project."

result = generate_report()
abv.flush()

# Output in ABV: [REDACTED_PERSON] met with [REDACTED_PERSON] to discuss the project.
```

<img src="https://mintlify.s3.us-west-1.amazonaws.com/abv-2be93c70/images/q9asPzRWn1-4qICe8mQBN_image.png" alt="" />

## Example 3: Masking Email and Phone Numbers

You can extend the masking function to redact other types of PII such as email addresses and phone numbers using regular expressions.

```python theme={null}
import re
from abvdev import ABV, observe

def masking_function(data, **kwargs):
    if isinstance(data, str):
        # Mask email addresses
        data = re.sub(r'\b[\w.-]+?@\w+?\.\w+?\b', '[REDACTED EMAIL]', data)
        # Mask phone numbers
        data = re.sub(r'\b\d{3}[-. ]?\d{3}[-. ]?\d{4}\b', '[REDACTED PHONE]', data)
    return data

abv = ABV(
    mask=masking_function,
    api_key="sk-abv-...", # your api key here
    host="https://app.abv.dev" # host="https://eu.app.abv.dev", for EU region
    )

@observe()
def contact_customer():
    info = "Please contact John at john.doe@example.com or call 555-123-4567."
    return info

result = contact_customer()

print(result)
# Output: Please contact John at [REDACTED EMAIL] or call [REDACTED PHONE].

# Flush events in short-lived applications
abv.flush()
```

<img src="https://mintlify.s3.us-west-1.amazonaws.com/abv-2be93c70/images/haM-X27ixrPf1mo0KbBBE_image.png" alt="" />
