PII-Aware Support Copilot
Description
A support “copilot” agent can read only the minimal PII fields it needs from the CRM when a ticket is open and customer consent is present. Behavioral trust must be ≥ 80 and tokens are short-lived (“one-shot” per request flow).
A support “copilot” agent fetches only the minimum customer fields required to resolve an open ticket (e.g., name, email, city).
Access is permitted only when:
- The ticket is open,
- The customer has consented,
- The agent has the
crm_read_basic
capability, - The behavioral trust score meets a configurable floor, and
- The token is one-shot for that specific retrieval.
All policy decisions and field scopes are written to an audit log for later review and redaction checks. Open policies (OPA/Rego) make the constraints transparent and easy to change without altering the source codebase.
Business Value
Reduce breach risk and audit scope while speeding ticket resolution (right data, right time), with automatic logs for compliance review:
- Risk reduction: Data-minimization by default lowers breach blast radius and PCI/PII audit scope.
- Operational speed: Agents get the exact fields they’re allowed to see; no over-fetching or ad-hoc approvals.
- Compliance posture: Immutable, per-request audit entries simplify responses to DSARs and regulator inquiries.
- Config agility: Update allowed fields or trust floors via policy (no redeploys).
Why Not With Alternatives?
API keys/OAuth scopes cannot express field-level constraints plus behavioral trust in a single, verifiable, per-request check tied to a DID-based agent identity and one-shot token. OPA alone lacks strong, cryptographically verifiable agent identity and built-in audit lineage per decision:
- API keys/OAuth scopes can’t enforce field-level constraints tied to per-request consent and ticket state.
- CRM role permissions are coarse and static; they don’t incorporate dynamic risk (trust score) or one-shot tokens with standing privilege mitigation.
- OPA alone evaluates policy but doesn’t bind decisions to a verifiable agent identity (DID/VC) and auditable, single-use access tokens.
Policy
default allow = false
allow if {
input.action == "crm_read_basic"
input.capabilities[_] == "crm_read_basic"
input.resource == "db://crm/customers"
# Contextual constraints
input.context.ticket_status == "open"
input.context.customer_consented == true
# Behavior-based trust floor
input.trust >= 80
# Field-level filter (server-side enforcement)
input.context.fields_allowed == ["name","email","city","country"]
}
SDK Implementation
import { IronBookClient } from '@identitymachines/ironbook';
const client = new IronBookClient({ apiKey: 'your-api-key');
const policy = `
default allow = false
allow if {
input.action == "crm_read_basic"
input.capabilities[_] == "crm_read_basic"
input.resource == "db://crm/customers"
input.context.ticket_status == "open"
input.context.customer_consented == true
input.trust >= 80
input.context.fields_allowed == ["name","email","city","country"]
}
`;
(async () => {
const agent = await client.registerAgent({
agentName: 'support-copilot',
capabilities: ['crm_read_basic']
});
const up = await client.uploadPolicy({
agentDid: agent.agentDid,
configType: 'opa',
policyContent: policy,
metadata: { name: 'support-pii-policy', v: '1.0' }
});
const { access_token } = await client.getAuthToken({
agentDid: agent.agentDid,
vc: agent.vc,
audience: 'https://crm.internal/api'
});
// Get ticket and/or customer info
const decision = await client.policyDecision({
agentDid: agent.agentDid,
policyId: up.policyId,
action: 'crm_read_basic',
resource: 'db://crm/customers',
context: {
ticket_status: ticket.status,
customer_consented: ticket.didConsent,
fields_allowed: ['name','email','city','country']
},
token: access_token
});
console.log('ALLOW?', decision.allow, decision.reason);
})();
import asyncio
from ironbook_sdk import (
IronBookClient, RegisterAgentOptions, GetAuthTokenOptions,
UploadPolicyOptions, PolicyInput
)
API_KEY = "YOUR_API_KEY"
POLICY_CONTENT = '''
default allow = false
allow if {
input.action == "crm_read_basic"
input.resource == "db://crm/customers"
input.context.ticket_status == "open"
input.context.customer_consented == true
input.trust >= 80
input.context.fields_allowed == ["name","email","city","country"]
}
'''
async def main():
client = IronBookClient(api_key=API_KEY)
agent_vc = await client.register_agent(RegisterAgentOptions(
agent_name="support-copilot",
capabilities=["crm_read_basic"]
))
policy = await client.upload_policy(UploadPolicyOptions(
agent_did=agent_vc["agentDid"],
config_type="opa",
policy_content=POLICY_CONTENT,
metadata={"name": "support-pii-policy", "v": "1.0"}
))
token = await client.get_auth_token(GetAuthTokenOptions(
agent_did=agent_vc["agentDid"],
vc=agent_vc["vc"],
audience="https://crm.internal/api"
))
# Get ticket and/or customer info
decision = await client.policy_decision(PolicyInput(
did=agent_vc["agentDid"],
token=token["access_token"],
action="crm_read_basic",
resource="db://crm/customers",
context={
"ticket_status": ticket["status"],
"customer_consented": ticket["didConsent"],
"agent_trust_score": agent_vc["trustScore"],
"fields_allowed": ["name", "email", "city", "country"]
}
))
print("ALLOW?", decision.allow, "REASON:", getattr(decision, "reason", None))
asyncio.run(main())
Updated 6 days ago