Security

How your data stays yours

HireHenry is multi-tenant: many companies' data lives in one system. The only question that matters is what stops one customer's data reaching another's, and whether that something is a rule people follow or a rule the machine enforces.

Ours is enforced by the database, and checked against production continuously rather than at audit time. This page says exactly how, in enough detail that you can hold us to it.


Isolation is a database rule, not application code

Every row of customer data carries the tenant it belongs to, and PostgreSQL row-level security filters on it before any query sees a result. That matters because of what it removes: there is no code path, no forgotten WHERE clause, and no new endpoint that can accidentally return another company's rows. The filter is not in the query — it is underneath every query.

The application connects as a non-superuser role. This is the part that is easy to get wrong and invisible when you do: a superuser connection bypasses row-level security entirely, and everything keeps working, and nothing looks different. So we check the role itself, not just the result.

What the live check actually does

Our operational status endpoint scopes a connection to a tenant that does not exist and counts the rows it can see. Two things have to be true, every time:

This runs against the production database, not a copy, and not a test fixture. A passing check is a statement about the system you are using right now.

The database is not reachable from the internet

PostgreSQL publishes no port outside the application's private network. There is no public endpoint to find, credential-stuff, or scan. The only route to your data is through the application, which means the isolation above is the only door rather than one of several.

What the assistant remembers, and what it refuses to

HireHenry's assistant improves with use because it remembers how you like to be helped. That is a feature with an obvious failure mode, so it is built with three limits:

It is separated the same way, one layer deeper

Personalization lives in its own service with its own database, isolated by the same row-level security — and additionally by a composite foreign key, so a stored row's tenant cannot disagree with its user's tenant. A constraint prevents the mismatch rather than a report finding it afterwards.

Sensitive detail is removed before it is stored

Each tenant declares what must never be remembered, and the service enforces it: credentials, payment and bank details, government identifiers, health information, biometrics, precise location and more are stripped from text before it is summarised, embedded or written. It removes the sensitive span and keeps the rest, and it tells the caller which rule fired — so a filter failing upstream is visible rather than silent.

A guess is never presented as a fact

Anything the system infers about you stays marked as inferred and is never used until a person confirms it. A remembered preference you did not state cannot quietly shape an answer.

What you can see and what you can delete

We think the last one matters more than it looks. Being told your data is safe is a claim; being able to see who opened it is a fact.

Transport and browser security

How we think about this

Two principles do most of the work here, and both come from things that went wrong somewhere before they were written down.

A check that shares assumptions with the thing it checks will pass. Isolation is verified from outside the application, by a separate process, using a second tenant's credentials — because a test that asks the system whether it is correct will get the answer the system believes.

Silence is not success. Our checks alert on failing, and also on not running — a monitor that quietly stopped looks exactly like one reporting good news.


Reporting something

If you believe you have found a security issue, write to security@hirehenry.io with enough detail to reproduce it. We would rather hear it early and awkwardly than late and politely.

Home

Last reviewed 24 September 2026. If something on this page stops being true, it is a bug — tell us and we will fix the page or the system, whichever is wrong.