TurfAI User Guide
Modules

Data Shield

Tokenise PII before content reaches an LLM.

Status: GA (v3.0). Data Shield tokenises 8 structured PII types (L1), tenant word lists (L2) and free-text names, locations and organisations (L3 NER, Microsoft Presidio), plus customer-authored detectors and three domain packs. RAG chat, the public chatbot, Prompt Lab, file contents, and a customer-held key are not in v3.0 — they ship in v3.5. Read Limitations before relying on it for a compliance attestation.

What it is

Data Shield tokenises PII before a workflow, agent, or extraction call reaches the underlying LLM provider. The LLM sees opaque tokens (EMAIL_84d0a133, SSN_c95eb795); the caller sees the original values restored in the response. Data Shield records every event in an immutable audit log with entity counts per type — never raw values.

Tokenisation is deterministic within a request scope and isolated across scopes:

  • Within one call the same raw value always maps to the same token, so the LLM reasons about "the customer" coherently across messages in that call.
  • Across calls (and across tenants) the same value maps to a different token — there is no cross-call or cross-tenant token reuse. The scope key is {tenant_id}:{scope_type}:{scope_id}, so tenant isolation holds by construction.

In v3.0 the scope is per-request and ephemeral — the token map lives in memory for the duration of one request and is destroyed when the request frame ends. There is no on-disk token map, and no customer-held key yet. Consistency across documents or sessions requires the v3.5 KMS-backed persistent scope (see roadmap).

When to use it

Turn it on for any LLM step that may see personal or sensitive data — extraction over identity documents, agents handling customer records, free-form LLM tasks on user content. It is a core governance control.

Data Shield is opt-in and off by default. Decide deliberately:

  • Enable per node when one specific LLM node (llm_task, agent_task, extraction / classification / summarization) touches PII.
  • Require per workflow when every LLM node in a sensitive workflow must be shielded and you want the executor to fail the run if an author forgets. Use this for regulated workflows (insurance, legal, financial) where a single un-shielded node is a breach.
  • Set per pack when a Solution Pack should ship the policy so every instantiated workflow inherits it.

How it works

Process flow — tokenise, call, restore:

Call flow — through the gateway:

Sub-features

  • L1 detection across 8 entity types: EMAIL, PHONE, SSN, CREDIT_CARD, IP, AADHAAR, PAN, IBAN.
  • L2 gazetteer — tenant word lists (competitor names, product codes), case-insensitive, word-boundary matched.
  • L3 NER (Microsoft Presidio) — free-text PERSON, LOCATION, ORGANIZATION and more; DATE_TIME is opt-in.
  • Custom detectors — tenant-scoped patterns, word lists and smart recognizers, added without a deploy.
  • Detector packs — Insurance, HR and Financial essentials; one-click install / uninstall per tenant.
  • Admin UIs — /settings/data-shield (Simple wizard or Advanced tabs) and a live tokenisation demo at /data-shield/demo.
  • Three opt-in scopes — per-node, per-workflow (require on every LLM node), per-pack (manifest policy).
  • Audit log — counts by type, layers invoked, stage latencies, retention timestamp, correlation id; immutable after write.
  • Kill switches — disable gateway or audit; tune retention.
  • Recall-biased detectors — tuned to over-tokenise rather than miss (a false positive is restored on the return path; a false negative is a leak).

Configuration parameters

The three opt-in scopes

ScopeWhere you set itEffect
Per-nodeWorkflowBuilder → select an llm_task, extraction_task, classification_task, summarization_task or agent_task → Data Shield card → flip the switchTokenises that node's LLM calls. Optionally restrict to a subset of entity types via the chip row.
Per-workflowWorkflowBuilder → Resilience tab → Data Shield policy card → Require Data Shield on every LLM nodeExecutor fails any run whose LLM nodes haven't opted in (DATA_SHIELD_POLICY_VIOLATION).
Per-packdata_shield_policy in the solution-pack manifestEvery workflow instantiated from the pack inherits the policy onto its activity row; authors can refine per-instance.

Kill switches (operators only — off by default)

VariableServiceEffect
DATA_SHIELD_DISABLED=truellm-serviceBypass the gateway entirely — raw payload reaches the LLM, no audit row. Incident response only.
DATA_SHIELD_AUDIT_DISABLED=truedata-shield-gatewayTokenise normally, skip the audit emit. Use only if the audit DB is the bottleneck.
DATA_SHIELD_AUDIT_RETENTION_DAYS=NDMSOverride the default 30-day retention on new audit rows.

Both kill switches are off in normal operation. Any period they are on is an attestation gap and must be logged in the operator runbook.

The 8 L1 entity types (always on when the shield is enabled)

TypeDetectionRegion
EMAILRFC-5322 subset regexInternational
PHONERequires formatting (+ prefix, parens, or separators); bare 7-digit runs don't fireInternational
SSNUS SSN; rejects 000 / 666 / 9xx prefixesUnited States
CREDIT_CARD12–19 digits + Luhn validationInternational
IPIPv4 dotted quadInternational
AADHAAR12 digits + Verhoeff checksumIndia
PAN5 letters + 4 digits + letterIndia
IBANBasic IBAN structural checkInternational

L1 latency target: under 5 ms per request at the 95th percentile. L3 (Presidio NER) also catches names, locations and organisations; L1 always wins an overlap.

Tips & best practices

  • Require the policy on regulated workflows. Per-node opt-in is convenient but easy to forget; in insurance/legal/financial workflows set the Resilience-tab policy to required so a forgotten node fails the run loudly instead of leaking silently.
  • Review the audit log regularly. End users see their own activity at /account/activity; operators search across tenants at /audit. Use it to confirm the entity counts look right for each run (e.g. a customer-record agent should show non-zero EMAIL/PHONE). Rows are immutable and contain counts only — there are no raw values to leak.
  • Don't treat tokenisation as a PII classifier. Detectors are recall-biased: a 9-digit string that resembles an SSN may be tokenised even if it isn't one. "We tokenised it" means "candidate PII", not "this is definitely PII".
  • Know what v3.0 does NOT cover before signing anything: RAG chat, the public chatbot, Prompt Lab previews, file (file_urls) contents, and any customer-held encryption key. See Limitations.
  • Tune retention to your obligations. The DS audit row default is 30 days — shorter than the rest of the audit log. Set DATA_SHIELD_AUDIT_RETENTION_DAYS to your record-keeping requirement.

Concrete examples

Before and after: what the provider sees and what the caller gets

The author sends a payload with PII. The provider only ever sees tokens; the caller gets the originals restored.

BEFORE (your payload)
  "Email [email protected] or call +1-415-555-0142. SSN 086-12-3456."

WHAT THE LLM PROVIDER SEES (tokenised)
  "Email EMAIL_84d0a133 or call PHONE_1f9c2a07. SSN SSN_c95eb795."

AFTER (response restored to caller)
  "I have emailed [email protected] and noted SSN 086-12-3456 on file."

Within this one call, [email protected] always maps to EMAIL_84d0a133. In a different call the same address would map to a different token.

Enable it on a node and require it on the workflow

  1. WorkflowBuilder → select the agent_task node → Data Shield card → flip the switch on. (Optional: in the chip row, restrict to EMAIL, PHONE, SSN only.)
  2. WorkflowBuilder → Resilience tab → Data Shield policy → Require Data Shield on every LLM node.
  3. Run the workflow. If any LLM node is left un-shielded, the run terminates with DATA_SHIELD_POLICY_VIOLATION.

For a pack, declare it in the manifest instead so every instantiated workflow inherits it:

# solution-pack manifest
data_shield_policy:
  required: true
  entity_types: [EMAIL, PHONE, SSN, AADHAAR, PAN]

An audit-log entry (counts only — never raw values)

{
  "action": "data_shield.tokenise",
  "entity_counts": { "EMAIL": 1, "PHONE": 1, "SSN": 1 },
  "layers": ["L1"],
  "latencies_ms": { "tokenise_ms": 1, "roundtrip_ms": 1461 },
  "retention_days": 30,
  "correlation_id": "run_7af3c9e2",
  "provider": "claude",
  "model": "claude-opus-4-1"
}

There is no field anywhere in this row that holds [email protected] or the SSN — only the count { "SSN": 1 }.

Dependencies

  • llm-service — hosts the entry points (/api/v1/chat, /api/v1/extract) and calls the gateway.
  • data-shield-gateway — internal-only service that runs detection, tokenisation and audit forwarding.
  • DMS — audit log storage and retention, tenant detector storage; user activity view at /account/activity.
  • Workflow executor — enforces the per-workflow required policy.

Limitations

The following are known gaps in v3.0 — read them before relying on it for a compliance attestation:

  • RAG chat is not tokenised. Questions and retrieved context on the Knowledge Base / RAG path go to the LLM unmodified (v3.5).
  • Public chatbot and Prompt Lab previews are not covered — end-user messages and authored preview prompts reach the provider as-is (v3.5).
  • File contents are not scanned. With the shield on, a call that attaches file_urls is refused with HTTP 400 and a clear message — extract the text first and send it through the shield, or disable Data Shield for that workflow. In-boundary OCR is a v3.5 deliverable.
  • Phone formatting matters. A bare 7-digit string is left alone; add a custom pattern for unformatted phone shapes you actually see.
  • NER uses a small English-only model by default — fast, but it occasionally misses; operators can switch to the larger model (DATA_SHIELD_SPACY_MODEL) for higher precision.
  • Per-request scope only — no cross-call or cross-document persistence, and no customer-held key (both require the v3.5 KMS-backed scope).

Failure modes: detector error or gateway unreachable → fail-closed (HTTP 503); shielded call with file_urls → fail-fast (HTTP 400); audit emit failure → fail-open; required-policy violation → workflow fails terminal; unknown token in the response → passed through untouched. See Troubleshooting.

Troubleshooting

SymptomCauseFix
LLM call returns 503, run blockedDetector errored at ingress (regex panic, unicode edge case). Data Shield is fail-closed — a missed PII check could be a breach, so it blocks rather than leak.Retry; if it persists, capture the input shape and report it. As a last resort an operator can set DATA_SHIELD_DISABLED (incident only — logs an attestation gap).
Run succeeded but no audit row appearedAudit emit failed (DMS down, JWT/network). Audit is fail-open — observability, not correctness — so the chat is preserved and a warning is logged.Check the data-shield-gateway warning log and DMS health; the LLM call itself was correct and shielded.
Workflow fails with DATA_SHIELD_POLICY_VIOLATIONThe workflow requires Data Shield on every LLM node, but one node hasn't opted in. The executor enforces the author's declared contract.Enable the Data Shield card on the flagged node, or relax the Resilience-tab requirement if the node genuinely needs no shield.
A token-shaped string appears in the final responseThe model invented a string matching the token pattern; Data Shield does not own it, so it is passed through untouched to avoid false substitution.Expected behaviour. If the model hallucinates tokens often, tighten the prompt.
Same value got a different token in another run / session"Deterministic within request scope" — consistency holds only inside one request's ephemeral map. Across requests, sessions, and tenants the same value deliberately maps to a different token.Expected in v3.0. Cross-call / cross-document consistency requires the v3.5 KMS-backed persistent scope.

How to test

  1. Enable on a node. In WorkflowBuilder, select an llm_task, open the Data Shield card, and flip the switch on.
  2. Run with sample PII. Execute it over text containing an email and an SSN, e.g. "Contact [email protected], SSN 086-12-3456."
  3. Confirm tokens reached the LLM. Inspect the run / provider request and verify the payload shows EMAIL_* and SSN_* tokens — not the raw values.
  4. Confirm values were restored. Verify the response handed back to the workflow contains the original [email protected] and SSN.
  5. Inspect the audit log. At /account/activity confirm one data_shield.tokenise row with entity_counts: { EMAIL: 1, SSN: 1 }, layers: ["L1"], latencies, and a correlation id — and no raw values anywhere in the row.
  6. Verify the required policy. Set the workflow's Resilience-tab policy to required, leave a second LLM node opted out, run it, and confirm the run terminates with DATA_SHIELD_POLICY_VIOLATION.

Related pages

  • Governance — Data Shield is one of the platform's core governance controls.
  • Knowledge Base / RAG — note that RAG chat is NOT tokenised in v3.0; questions and retrieved context reach the LLM unmodified until v3.5.

Coming soon (forward-looking; not part of the v3.0 attestation):

CapabilityTarget
RAG chat, public chatbot, and Prompt Lab path coveragev3.5
In-boundary OCR + file-content tokenisationv3.5
Customer KMS vaultv3.5
Persistent document scope (cross-document consistency)v3.5
Hash-chained evidence-pack exportv3.5
L4 commercial detector, L5 local-LLM detector, sensitivity-based routing, image redactionv4.0

APIs used

MethodPathPurpose
POST/api/v1/chatDirect LLM chat (shielded)
POST/api/v1/extractStructured extraction (shielded)
(view)/account/activityUser's own tokenisation activity
POST/api/data-shield/previewLive tokenisation demo (DMS proxy)
(view)/auditCross-tenant audit search (operators)

See the synced Data Shield reference and architecture/data-shield/spec.md for the full spec.

On this page