Security, customer data and GDPR
Secrets, personal data, permissions and GDPR: how to set up an AI agent safely, and the actions it must never take alone.

An agent is a program that holds your keys, reads your customer data and can act in your tools. Treat it like a new hire with production access on day one: it gets the minimum it needs, it never sees a secret in clear text, and anything that cannot be undone waits for a person. Most of the security work is boring plumbing done once. The rest is a short list of actions you decide, in writing, the agent will never take alone.
None of what follows is legal advice. I describe how I set things up and what I check; your counsel decides what your company is allowed to do with its data.
Keep secrets out of the agent’s context
A secret pasted into a prompt ends up in a conversation log, maybe in a vendor’s retention, and sometimes in a file the agent writes back to disk. The rule I follow: the model never reads a key. It calls a tool, and the tool reads the key from the environment.
| Where secrets go | Where they never go |
|---|---|
| Environment variables or a secret manager read by the tool | The instructions file or any prompt |
A .env file listed in .gitignore |
Example configs committed to the repo |
| Scoped keys, one per agent or routine | A shared admin key reused everywhere |
Scoped keys pay off the day something goes wrong: you revoke one key and one agent stops, instead of everything at once.
The case that convinced me was not a leak. For a while, the whole agent organisation that runs Derrick’s marketing (I co-founded Derrick) existed in one copy, on one laptop. Since November 2026 it lives in a private repository, with customer data excluded and a check that blocks the push if a secret appears. A minimal version, as a Git pre-push hook:
#!/bin/sh
# .git/hooks/pre-push: refuse to push if a tracked file looks like it holds a secret
PATTERN='(sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----)'
if git grep -nIE "$PATTERN" -- . ':!*.lock'; then
echo "Possible secret found. Push blocked."
exit 1
fi
A dedicated secret scanner catches more patterns than this. The point is that the check runs every time without anyone remembering to run it.
Send the model only the data the task needs
Most agent tasks do not need personal data at all, and the ones that do rarely need all of it. Before an agent reads a CSV export, a CRM dump or a log, I have a script produce a digest: the columns the task uses, aggregated where possible, with emails, phone numbers and tokens replaced. The agent works on the digest. It is cheaper in context, and a model cannot repeat what it never saw.
import re
EMAIL = re.compile(r"[\w.+-]+@[\w-]+\.[\w.-]+")
PHONE = re.compile(r"\+?\d[\d .-]{7,}\d")
def redact(text: str) -> str:
text = EMAIL.sub("[email]", text)
return PHONE.sub("[phone]", text)
Regex redaction only catches the obvious: names and free-text notes slip through. So the better move is upstream, by choosing which columns leave the database in the first place. More on what the agent should read and where facts live in the memory and context chapter.
When personal data has to reach the model, as in prospecting, check three things with your vendor and your counsel: whether inputs are used for training, how long they are kept, and where they are processed. Those answers belong in your record of processing, next to the legal basis you rely on.
Give each tool the smallest permission that works
An agent can do whatever its tools allow. So the permission model lives in the tools, not in the prompt. “Do not delete anything” in an instructions file is a wish; a tool that has no delete endpoint is a guarantee. I write the policy down per agent, in a file a reviewer can read:
agent: outbound_assistant
tools:
crm:
allow: [search_contacts, read_contact, create_note]
deny: [delete_contact, merge_contacts, update_owner]
sending_tool:
allow: [read_campaigns, read_audience, add_lead_draft]
deny: [launch_campaign, send_message]
files:
allow_read: [data/digests/]
allow_write: [out/]
human_approval_required:
- any action on more than 50 records
- anything that sends, publishes, deletes or spends money
If you build your own connector, this is where design matters most: expose narrow tools with explicit arguments rather than one generic “run query” endpoint. Building your own MCP server covers how to shape those tools.
Treat everything the agent reads from outside as data, never as instructions. A web page, an inbound email or a CRM note can contain text written to steer a model. The defence is structural: the step that reads untrusted content has no write tools, and its output is a structured field that the next step validates.
The actions an agent never takes alone
Here is my list. It is short on purpose, so it is actually enforced.
| Action | Why it waits for a person | What the agent does instead |
|---|---|---|
| Sending a message to a prospect or customer | Your name, your domain reputation, your legal exposure | Drafts it, a person sends |
| Publishing content | Wrong facts stay indexed and quoted | Opens a draft or a pull request |
| Deleting or overwriting records | Usually irreversible | Lists what it would change |
| Spending money or credits above a ceiling | Runaway loops are cheap to start | Stops and reports the estimate |
| Changing permissions, keys or integrations | Widens what every other agent can do | Writes the request |
Scheduled routines follow the same rule: a routine proposes, it does not publish or send unless there is a written exception for that routine. One of the real checks in our outbound flow sits right here: before pushing leads to the sending tool, the agent verifies it will not overwrite a lead that is already in an active campaign. Running the agent on its own shows how routines check their tools before they start.
GDPR and the AI Act: the questions to put to every agent
Agents do not change the rules on personal data. They make it easier to process more of it, faster, which makes the existing rules bite sooner. The questions I put to every agent that touches personal data:
| Question | Where the answer lives |
|---|---|
| What personal data does this agent read, and why does it need each field | The agent’s spec and policy file |
| What legal basis covers that processing | Your record of processing, validated by counsel |
| Which vendors receive the data, under what terms | Data processing agreements |
| How a person can object or ask for deletion, and how the agent respects it | An opt-out list the agent reads before every run |
| How long outputs are kept | A retention rule on the output folder or table |
For France, the CNIL’s AI resources and its page on commercial prospecting by email are the places to start; the European Commission’s data protection page covers the GDPR itself. The EU also has an AI regulation with obligations that depend on the use case and the risk level; the Commission’s AI Act page explains the framework. I am not qualified to tell you where your use falls, and neither is your agent. If your TAM is involved, the opt-out and freshness logic in keeping the TAM up to date applies unchanged.
A one-page review before the first run
Before any agent touches real data, I fill this in and keep it next to the instructions file.
| Check | Done when |
|---|---|
| Secrets | No key in prompts or repo; push blocked on a secret pattern |
| Keys | One scoped key per agent; you know how to revoke it |
| Data in | List of fields the agent reads, each one justified |
| Redaction | Digests strip contact details and tokens before the model sees them |
| Tools | Allow and deny lists written; no generic write endpoint |
| Untrusted content | Steps that read it have no write tools |
| Human gates | The table of actions above, with the threshold numbers filled in |
| Logs | Every run records what it read, what it changed and who approved |
| Legal | Counsel has seen the data list and the vendors |
Your next step: take the agent you are building, write its policy file with allow and deny lists, and remove one permission it has today that it does not strictly need.