Sensitive data
VirtuAI scans every stored message for secrets and personal data, masks what it finds in the Conversations screen and in exports, and records who looks at it. This page goes into the detail. For finding and reading conversations, see Conversations.
What this does and doesn’t cover
Section titled “What this does and doesn’t cover”Detection runs when a message is stored, on every channel that stores messages. It uses patterns and checksums, not a model, and it sends nothing to any outside service.
It controls what people see when they audit conversations. It doesn’t change the conversation itself:
- The agent and the model provider receive the original text.
- The person chatting sees what they typed.
- The transcript is stored as written. What’s stored separately is a finding: the category and the position of the value, never a copy of it.
If a secret shows up in a conversation, treat it as exposed and rotate it.
Categories
Section titled “Categories”| Category | Severity | Default | What’s detected | Visible after masking |
|---|---|---|---|---|
| Credit card | Critical | On | 13 to 19 digits, with or without spaces or dashes, that pass the card checksum (Luhn) | Last 4 digits |
| API key | Critical | On | Keys that start with sk-ant-, sk_live_, sk-, AIza, AKIA, ghp_ and the other GitHub token prefixes, xoxb- and the other Slack prefixes, glpat-, and JSON Web Tokens |
Nothing |
| Private key | Critical | On | The -----BEGIN ... PRIVATE KEY----- line of a PEM key |
Nothing |
| Password | Critical | On | The value after password, passwd, contraseña, clave or secret followed by : or = |
Nothing |
| Bank account (IBAN) | High | On | IBANs that pass the mod-97 check | Last 4 characters |
| National ID | High | Off | Mexico CURP, Brazil CPF, Chile RUT, and Spain DNI with a valid check letter | The last 1 to 4 characters, depending on the format |
| Phone number | Medium | Off | Numbers in international format, starting with + |
Last 4 digits |
| Email address | Low | Off | Email addresses | Nothing |
| IP address | Low | Off | IPv4 addresses | Nothing |
The optional categories are off because they’re specific to a country or match ordinary text too often. An IP address and a version number such as 1.2.3.4 look the same, for example.
When two patterns match the same text, the more severe one wins, so a card number isn’t also reported as a phone number.
How masking works
Section titled “How masking works”Masking happens on the server, before the conversation is sent to your browser, so the real value isn’t in the page or the network response.
- A masked value becomes dots. Where a tail is kept, you see it at the end:
••••••••••••1111. - It applies to everything built from the message, not only the transcript: the conversation title and the preview in the list.
- Titles and previews are shortened copies of a message. If the cut falls in the middle of a card number or a key, the part that’s left is masked too.
A conversation with findings gets a badge for its most severe category, and the Sensitive data panel lists what was found by category. Filter the list with With sensitive data to see only those conversations.
Revealing a value
Section titled “Revealing a value”To see a masked value in full:
- Open the conversation. This requires
conversations.audit. - In the Sensitive data panel, select Reveal next to a category. This requires
conversations.pii_reveal. - Confirm with Reveal and record.
You reveal one category at a time, and only in that conversation. Every reveal is written to the access log with your user, the time and the category. There’s no setting that turns masking off.
Exports
Section titled “Exports”Exports are masked the same way as the screen.
| Export | Permission |
|---|---|
| Export CSV: the filtered conversation list | conversations.manage |
| Export Markdown or Export JSON: one conversation | conversations.manage and conversations.audit |
Exports from the screen are always masked. An unmasked export is possible only through the API, only for people who also hold conversations.pii_reveal, and the access log records which categories were included.
Choosing what’s detected
Section titled “Choosing what’s detected”Rules are set per workspace in the conversations.sensitive_rules setting: a JSON object that turns each category on or off. Categories you leave out keep their default.
{ "phone": true, "email": true, "national_id": true, "password": false}The category names are credit_card, api_key, private_key, password, iban, national_id, phone, email and ip_address.
There’s no screen for this setting yet. A workspace admin with settings.manage can set it through the settings API, or ask support to set it.
Re-scanning
Section titled “Re-scanning”Rules apply to messages stored after the change. To apply the current rules to a conversation stored earlier, open it and select Re-scan. This requires conversations.manage, replaces that conversation’s previous findings, and is recorded in the access log.
Access log
Section titled “Access log”Access log, in the Monitor section, lists every time someone:
| Action | Meaning |
|---|---|
| Viewed | Opened a transcript |
| Revealed | Unmasked one or more categories. The categories appear under Details. |
| Exported | Downloaded a conversation or the conversation list |
| Re-scanned | Applied the current rules to a stored conversation |
Each entry shows when, who, the action and the conversation. Filter by Action to narrow it down. Reading the log requires conversations.audit, because it shows who looked at which conversation.
Permissions
Section titled “Permissions”| Permission | Allows | Default roles |
|---|---|---|
conversations.view |
Listing conversations | admin, user, viewer |
conversations.audit |
Opening any transcript, the findings report and the access log | admin |
conversations.pii_reveal |
Revealing masked values | admin |
conversations.manage |
Exporting, re-scanning and applying retention | admin |
See the Permissions reference and the Security overview.
