Security

It reads your production database. Here is what stops it doing anything else.

ReplyPen answers support email from your real data. Any good CTO will want to know what happens when the model is wrong, or when a customer email tries to hijack it. This page separates what the kernel and the database engine enforce from what depends on a model behaving, and says what we do about the second part.

Last verified: 3 September 2026 · Operated by ProBackup BV, SOC 2 Type II
The short version

Six things that are true on day one

Every one of these is the default. Loosening any of them is a setting you change per project, later, if the quality earns it.

Nothing is sent without a person

Replies land as a draft in your own inbox. Someone on your team reads it and clicks send. Automatic sending is a switch you can turn on later, per project.

Nothing in your database changes by itself

The model works with read-only database access. Changes go through scripts you approved in advance, and by default each one waits for a human to click confirm.

It only sees the tables and columns you allow

You declare what it may read. Everything else does not exist from its point of view. Your own database enforces this, not a prompt.

Each of your customers sees only their own rows

When a customer asks something, their database view is filtered to their rows before the model reads a single byte. It cannot widen that view, whatever the email says.

Personal data can be masked before it reaches the model

Names, emails and phone numbers in columns you mark are replaced by tokens on the wire. The real values are put back only in the finished draft.

No AI company trains on your data

Two model providers, one in the EU and one in the US, both under business terms that forbid training. The full list, and everything else we keep, is on Where your data goes.

ProBackup BV · audited SOC 2 Type II
Audited

SOC 2 Type II, held by the company that runs ReplyPen

ReplyPen is built and operated by ProBackup BV, a Belgian company whose team has backed up SaaS data for other businesses since 2017, first under the Momentum Tools brand and since 2020 as Pro Backup. That company holds a SOC 2 Type II report, re-examined by an external audit every year, and a public trust center where you can request it, along with the control list, the penetration test summary and the data processing addendum.

  • Encrypted at rest with AES-256, TLS in transit
  • Hosted on Amazon Web Services in Frankfurt, one isolated network, nothing shared with other products
  • Multi-factor authentication and documented approval for every production access
  • Cyber insurance in place
  • Incident response and disaster recovery plans, reviewed yearly
For your CTO

Guaranteed, or merely hard. We keep the two apart.

Some of our controls are enforced by a kernel, a firewall or your database engine. A language model cannot talk its way past them. Others rely on a model or a classifier doing the right thing, and those can fail. Both lists are below, and we say which is which.

Enforced. The model cannot opt out.

What holdsHowEnforced by
A fresh container per run, destroyed afterwards Non-root user, all Linux capabilities dropped, no new privileges, read-only root filesystem, gVisor sandbox between the run and the host kernel. Teardown runs on every exit path, including crashes. Docker, gVisor
Read-only database access The run receives a one-time token for our database proxy. The real connection string never enters the container. The role behind the proxy can only select. PostgreSQL or MySQL, our wire proxy
Only declared tables and columns exist Before each run we mint a short-lived database login and a private set of views over exactly the tables and columns you declared. The base tables answer permission denied. PostgreSQL and MySQL 5.6.0+ are shielded the same way. PostgreSQL or MySQL
One customer, one set of rows Tenant and end customer filters are baked into those views before the run starts, from an identity the host verified. The model never supplies the filter. PostgreSQL or MySQL
Your database stays fast We care about your uptime as much as you do. Every query is cancelled on your server after 30 seconds, not merely abandoned by us. At most two connections per run, results capped at 50,000 rows, read-only transactions. A heavy query fails; your database does not. A read replica is welcome. PostgreSQL or MySQL, our wire proxy
No write path through the model Our knowledge repository and your source mirrors are mounted read-only. A write returns a filesystem error. The only write path is an action: a script pinned by its sha256 that you approved, executed with credentials the run never holds. Linux mounts, our action runner
Every outbound connection goes through our gateway A default deny firewall inside the container network namespace. In enforce mode only allowlisted hosts pass. Internal and cloud metadata addresses are refused regardless. Every attempt is logged with host and verdict. Linux netfilter, our gateway
Secrets stay out of the model Your credentials are encrypted with AES-256-GCM at rest and injected into the container environment only. They never appear in command arguments, logs or the model's context. Our host process
Personal data masked by column Columns you mark are tokenized inside the database wire proxy before any byte reaches the container. Real values return only in the delivered draft. Our wire proxy
Autonomy switches live on our side Draft versus send, and human, policy or auto per action, are settings the host checks at the moment of delivery or execution. The model has no way to change them. Our host process
Everything is on record Every command, every network attempt and every action is stored per project and available over the API. Raw content is deleted after 14 days. Postgres, our retention sweep

Probabilistic. A model is still in the loop here, and this is what we bolt on.

Being specific about failure is the point of this section. Read the first column as the risk, the second as the ceiling on the damage, the third as what we add.

What can go wrongWorst case, given the list aboveWhat we add
A customer email, or a web page the model fetches, contains instructions and the model follows them It reads the scoped view it already had and talks to hosts on the allowlist. It cannot reach other tables, other customers, your credentials or an arbitrary server. An input screen on inbound content as defense in depth, never as the only control. A blocked connection produces a visible note on the run, not a silent failure.
The model misreads your data and drafts a wrong answer A wrong draft in your inbox. With auto-send on, a wrong email to a customer. Every draft shows what it checked and where each fact came from. Drafts stay the default until your own edit rate says otherwise. Your corrections feed the next run.
In a customer-facing chat, the model paraphrases how your product is built: a file, a schema, a column list A description of your internals in a chat answer. Not the data itself, which is covered above. A rule in every prompt, a deterministic output guard, a check that no attachment copies your source, and a second model that judges the answer. That last judge can miss. We say so, and every block raises an alert to us.
Personal data outside the marked columns reaches the model The email sender's own details and values in computed or joined columns are not masked. A database we cannot shield is refused rather than used unmasked. Each unmasked path is flagged in the run trace so you can see it. Masking is by declared column and we do not claim it catches free text.
An action on policy mode runs with a wrong parameter A change inside the bounds of the policy you wrote, for example a refund below your cap. The policy is one line of code you wrote, not a model judgement. Caps and human mode are the defaults. Every execution is logged with its parameters.

The strictest setting

This is what a project looks like with every switch closed. You can open any of them later, per project, without a deploy on your side.

Project settings · Security
Automatic sending Off. Drafts only
Actions on your system Off
Internet from the run Off. Only your database host
Every table and column Off. Allowlist, secrets excluded
Raw personal data to the model Off. Masked
Rows of other customers Off. Filtered per customer
Model provider Always on. Google EU or OpenAI USsee where your data goes

The model provider is the one connection that cannot be switched off, because it is the model. Google already runs in the EU; the OpenAI leg moves to OpenAI's EU residency endpoint as a configuration change once our application is approved, which we expect in a matter of weeks. Every other switch here opens something you may want later.

How one customer's question sees one customer's rows

The filter is not a prompt. It is a database view, minted per run, from an identity our host resolved before the model started. The example uses a dental software company with many practices.

Your database

Every table, every row

patients appointments invoices users api_keys audit_log

40 000 patients

Project projection

Only declared tables and columns

patients appointments invoices usersid · name
password_digest
api_keys audit_log

Tenant filter

WHERE practice_id = :scope

Bright Dental Antwerp Smile Clinic Gent Tandarts Peeters Mondzorg Leuven 26 more practices

1 practice · 1 480 patients

Customer filter

AND patient_id = :patient_id

Emma Janssens appointment 3 Sep 14:00 appointment 11 Jun 09:30

1 patient

A database login that can only read stage 4 is what the run logs in with. Asking for stage 1 returns permission denied.

How we attack ourselves

We do not wait for a yearly test. AI agents run break-in attempts against our production, on the real machine, every day: container escape, network egress, credential theft, reaching one project from another. Findings go to a human the same day and are fixed with a retest before they are closed.

We publish the results because we would want to read this about a vendor. In June 2026, during our internal-only pilot, the first full assessment found two critical issues. The network gateway that should filter and log every outbound connection could be bypassed from inside the container, and the container could reach the cloud metadata service and read the machine's credentials. Both were fixed and verified live within four days. The container escape battery held. The same battery runs again on every assessment, and a regression is treated as the headline.

2
critical findings, June 2026
4 days
to fix and verify both
0
container escapes, every run since

Let your own AI check us

Prefer to let your own AI check us? Paste the prompts on Ask your AI into the coding agent that knows your codebase. Every page on this site has a plain-text twin it can read, listed there.

Questions your CTO will ask

Do you need write access to our database?

No. Runs use a read-only role. We also hold a provisioning credential that can create roles and views, used only by our host process and never by a run. Everything we add is our own objects. Drop them and your database is byte for byte what it was.

Will it put load on our database?

Very little, and never unbounded. A query that runs longer than 30 seconds is killed on your server, on MySQL by a KILL QUERY from our proxy, on PostgreSQL by statement_timeout. A run holds at most two connections. We tested this on a customer's MySQL 5.6 server with deliberately abusive queries and watched them vanish from the process list at 30 seconds. Point us at a read replica if you prefer.

Do you install anything in our application?

Not for reading. If you want actions, there is an optional library for your framework that executes approved scripts inside your own runtime. Every call to it is signed with a per-project secret and verified with a constant time comparison.

Where does it run?

Amazon Web Services in Frankfurt, one isolated network, production only. Model calls go to Google in the EU and to OpenAI in the US under standard contractual clauses. Nothing else sees email content.

Can our developers see what it did?

Yes. Every command it ran, every network attempt with its verdict, and every action with its parameters are on the run trace and the API. The knowledge it keeps about your product is a git repository you can read and edit.

What access do you need to our mailbox?

Enough to read the inbox and place a draft in it. On Gmail that is one scope, gmail.modify: read, draft and label. It cannot touch account settings or permanently delete mail. It could technically send, which is why send-or-draft is enforced on our host, off by default, and never readable by the model. On Outlook it is the equivalent Graph mail permission. If you would rather grant no OAuth at all, connect a mailbox or alias over IMAP with its own app password.

Our database host cannot allowlist an IP. Now what?

Three options, in the order we suggest them. A read replica on a host that can allowlist. An SSH bastion hop, which we support for MySQL today and for PostgreSQL on request. Or TLS plus a dedicated role whose standing credential is only ever used by our host process: each run gets its own short-lived login minted from it, so the credential you handed over is never inside a run.

How do we look up the sender's account without exposing email addresses?

Declare the email column and mark it as personal data. The lookup by the sender's address still runs inside your database, and the model only ever sees a token where the address would be. The sender wrote their own address in the email, so nothing new is revealed. Tenant matching in a multi-tenant setup comes from a trusted signal we control, a per-tenant mailbox or a signed token, never from anything read out of the thread.

Are the personal-data tokens stable? Can the model recognise the same person twice?

Yes, per project and tenant. A token is a keyed hash of the value, with a key derived per project and tenant, so the same person gets the same token across runs within that scope and a different one in another project or tenant. The model can therefore tell that two threads concern the same account without ever seeing a name or an address; the mapping back to real values lives only in our host's memory for the duration of a run. Stable pseudonyms are still personal data under the GDPR, and our DPA treats them as such.

Does our source code go to the model providers?

The parts a run reads, yes: the model cannot reason about code it has not seen, and that is where most of the value comes from. Three things bound it. You choose which repositories to mirror, and a docs-only or support-relevant subset is a normal choice. Both providers work under business terms that forbid training and retention beyond the request. And the reply path is guarded against code, file names and schemas appearing in an answer (the probabilistic table above says how, and where that guard can miss). If a self-hosted model in the EU is a requirement, ask: the grounding work does not need a frontier model, and we plan for that option.

What is left after the 14 days?

The run's timeline: event types, timestamps, and a SHA-256 plus byte count of each scrubbed payload, so you can still prove what ran and when. That timeline is kept for at most twelve months, then deleted too; earlier on request. Network and API logs are deleted outright. Email bodies, query output, drafts and model reasoning are gone. Database backups are encrypted, stay in Frankfurt and roll off after seven days, so nothing scrubbed outlives a backup by more than a week.

What happens if we leave?

Drop our role and schema. Within 30 days of your written request we delete the knowledge repository, the project's rows and the audit timeline, and confirm it in writing. Raw email content on our side is gone after 14 days anyway. Nothing was installed and nothing in your system was modified, so there is no migration back.

Is there a volume minimum?

No. A pilot is one project, one mailbox and a handful of tables, and a few threads a week is a normal starting volume. The paperwork is the same DPA either way.

What do we tell our own customers?

Name us as a processor in your privacy policy, with Google and OpenAI as our sub-processors for the AI step. One sentence usually does it: "Support requests may be processed by ReplyPen (ProBackup BV, Belgium), which uses Google Cloud (EU) and OpenAI (US, under Standard Contractual Clauses) to draft replies; none of them train models on your data." We send the DPA and the sub-processor list in writing.

How do we report a vulnerability?

Write to security@replypen.com. A person reads it, you get a reply within one business day, and we will tell you what we changed. We do not run a bounty program at this time.

Bring your CTO. We prefer the hard questions up front.

This page describes the system as deployed on the verification date at the top. Where it summarises another company's terms, that company's own policy is the authoritative text. Legal detail lives in the Privacy Policy and Terms of Service.