Security
The record is the product. This is how it is held.
Consential exists to produce one thing a practice can hand to an investigator: a timestamped record of what a visitor was asked and what they chose, which nobody can quietly edit afterwards. That record is worth exactly what its integrity is worth. Everything below serves that single claim, and we have kept this page to controls that are running today.
Last reviewed 30 July 2026 · changes to this page are dated, not silent
-- The consent_events table is the legal product.
-- These REVOKEs are the enforcement, not a convention.
REVOKE UPDATE, DELETE ON consent_events FROM app_user;
REVOKE UPDATE, DELETE ON consent_events FROM audit_reader;
- CONSENT RECORD
- Append-only
UPDATEandDELETErevoked at the database permission level. - PATIENT DATA
- None heldNo name, email, phone, raw IP address or clinical detail in a consent record.
- IN TRANSIT
- TLS 1.2+Every public route, with no plaintext listener to fall back to.
- BACKUPS
- Verified nightlyEvery backup is restore-tested automatically; a bad one fails the job loudly.
01 — ARCHITECTURE
Four zones, and the data only moves inward
Most consent tools are a JavaScript file and a database sitting next to each other. Ours is deliberately segmented, because the interesting question in a breach is never "was there a wall" — it is "how many walls, and what was behind each one."
The database that holds consent records publishes no port and has no route to the public internet. It is not addressable from outside, correct credentials included; the only thing that can open a connection to it is the application, over a private network the two of them share. The application in turn has no public port of its own — every request from the outside world arrives through the edge, where TLS terminates. If the application were fully compromised tomorrow, the attacker would inherit an account that is allowed to read and insert consent events and is not allowed to change or delete a single one.
02 — WHERE IT RUNS
We do not run a data center. That is the point.
Operating our own server room would be a liability, not a credential. Consential runs on infrastructure inside professionally operated commercial data centers. Physical security — the fence, the gatehouse, the biometric readers, the 24-hour guards, the power and cooling redundancy, the drive destruction on decommission — is the provider’s responsibility, carried out at a scale and under an independent audit regime that a software vendor running its own hardware cannot match.
Consential.io and the consent widget are served from Amazon Web Services, and we have an executed Business Associate Addendum with AWS, active since 13 July 2026, covering the AWS services we build on. That is a hard constraint on which services we are permitted to use, not a preference: it is the reason we are not on offerings excluded from the HIPAA-eligible list, and the reason a host that will not execute a Business Associate Agreement is not somewhere this product will run.
About these images. They are illustrative renderings of commercial data center infrastructure, not photographs of the specific facilities that host Consential. Providers at this tier do not disclose data center locations or permit vendor photography inside them, and we would rather say so plainly than imply we have been given a tour.
Our infrastructure providers are the only third parties that touch consent records. No analytics vendor, no session recorder, no outsourced support desk and no third-party tool has access to them. The current sub-processor list forms part of our Data Processing Addendum and is set out field by field in the privacy hub, and it changes with notice, not quietly.
03 — SHARED RESPONSIBILITY
Three parties, and none of them can cover for the others
Security vendors get vague here, because a clear division of labour makes it obvious what the vendor is not doing. We would rather you know the boundary before you sign than discover it during an incident.
LAYER 1
The infrastructure provider is responsible for
- Physical facilities, perimeter and personnel access
- Power, cooling and hardware lifecycle
- Hypervisor and host operating system
- Physical destruction of decommissioned media
- Their own third-party audit programme
LAYER 2
Consential is responsible for
- Network segmentation and what is exposed at all
- Append-only enforcement on the consent record
- Keeping one practice’s data away from another’s
- Credential handling and rotation
- Patching, backups and restore verification
- The integrity of the widget we publish to your site
LAYER 3
Your practice is responsible for
- Who on your staff holds a dashboard login
- Removing access when someone leaves
- What your privacy policy actually says
- Deciding which trackers you run at all
- Your own HIPAA programme, of which this is one part
A signed Business Associate Agreement is what makes layer 2 legally binding rather than a marketing promise. We execute one with every practice that requires it before any data flows. Beneath us, our infrastructure providers make the equivalent agreement available for the services we build on, which is the mechanism that carries the same obligation down to the hardware. The chain has to be complete end to end or it is not a chain.
04 — ENCRYPTION & SECRETS
Encrypted on the way in, and no credential in a file you could read
Encryption in transit to the browser is table stakes; every vendor has it. What is worth checking is whether there is any path that quietly falls back to plaintext. There is not: there is no HTTP listener to fall back to, so a misconfigured client fails loudly instead of succeeding insecurely.
| In transit | Protocol | Enforced by |
|---|---|---|
| Patient browser → widget CDN | TLS 1.2+ | CDN policy, HTTPS only, no plaintext origin |
| Widget → API | TLS 1.2+ with HSTS | Edge proxy; no plaintext listener exists |
| WordPress plugin → API | TLS 1.2+ | Certificate verification enforced in the client |
| API → database | Private network | No published port; not addressable from outside the host |
| Outbound webhooks to your CRM | TLS 1.2+ | HTTPS only, no HTTP fallback, HMAC-signed payloads |
Credentials live in a root-owned environment file with permissions set so that no other account on the host can read it. No database password, signing key or API token is committed to source control, written into a configuration file that ships with the application, or stored in any document store. The database superuser — the only role on the system that could alter a consent record — is not the account the application runs as, and its credentials are not held by the running service.
A note on what encryption does and does not buy you. Encryption at rest protects against a stolen disk. It does nothing against an attacker who has reached the running application, because at that point the data is decrypted for them by design. The control that actually protects a consent record from being rewritten is the permission model in the next section, and that is where we have put the emphasis.
05 — RECORD INTEGRITY
A log you can edit is not evidence
Any consent tool can show you a list of consents. The question an investigator actually asks is different: could this list have been changed after the fact? If the honest answer is yes, the list proves nothing, and a vendor dashboard that lets staff "clean up" a record has quietly destroyed the only thing it was built to produce.
So the consent table is append-only, enforced where it cannot be argued with
— at the database permission level, not in application code that a future bug or a
rushed feature could bypass. The account our API runs as has been granted
SELECT and INSERT on consent events, and
UPDATE and DELETE are additionally and
explicitly revoked from it, so the restriction survives anyone later granting the role
blanket table permissions by mistake. There is no admin screen that overrides this, because
there is no admin account that overrides it.
This is not a design note. It is verified by a script that connects as the
application’s own database role and attempts a real UPDATE
against a real database, confirming that PostgreSQL itself refuses it. That check runs as part
of the test suite, so a change that silently loosened the guarantee would fail the build
rather than ship.
| Table (application role) | SELECT | INSERT | UPDATE | DELETE |
|---|---|---|---|---|
| consent_events | YES | YES | REVOKED | REVOKED |
| policy_versions | YES | YES | NO | NO |
| practices | YES | YES | YES | NO |
| visitor_consent_current | YES | YES | YES | NO |
| webhooks | YES | YES | YES | YES |
Note the two ends of that table. Webhook destinations are configuration, so they are fully editable — a practice must be able to change where its data is sent. The two rows that constitute the legal record cannot be touched at all. And a "current state" table exists separately precisely so that showing a visitor’s latest choice never requires overwriting the history of how they got there.
That separation is the whole mechanism, and it is easiest to see in one visitor’s history. A change of mind is a new row. A withdrawal is a new row. Nothing is ever revised in place, which is why the record can still answer a question asked years later.
06 — WHAT WE STORE
The whole consent record, field by field
The strongest security control is not collecting the data in the first place. A consent event is deliberately narrow: it records a choice, not a person. Here is every field, so you can see the shape of it rather than take our word for the summary.
| Field | What it holds | Why |
|---|---|---|
| visitor_id | A random, site-scoped identifier — not a fingerprint | Lets a returning visitor see and change their own choice |
| ip_hash | A SHA-256 hash of the IP address, never the address itself | Distinguishes one visitor from another without retaining a network identifier |
| ip_geo_region | A US state code, derived at the moment of capture | State privacy laws differ; the record shows which rules applied |
| policy_version | A hash of the exact policy text shown | Proves which wording the visitor agreed to, not just that they agreed |
| consent_action | granted, denied, revoked or updated | A revocation is a new record, never an erasure of the old one |
| categories | The per-category choices, such as analytics or marketing | Consent is rarely all-or-nothing |
| source | widget, preference centre, API or Global Privacy Control | Shows how the choice was expressed, including automated browser signals |
| user_agent | The browser string | Corroborating detail if a record is ever questioned |
| created_at | A UTC timestamp | The record is only evidence if it is anchored in time |
What is not in that table matters as much as what is. There is no patient name, no email address, no phone number, no date of birth, no raw IP address, no page URL, no appointment detail and nothing describing a condition, a treatment or a procedure. Consential is not an intake form and is not a place to put clinical information. If you are looking at a field and wondering whether health information could end up in it, the answer we have engineered for is no.
The policy version field deserves a second look, because it is the one most tools omit. Recording that someone clicked "accept" is nearly worthless if you cannot establish what they were shown when they clicked it. Hashing the policy text at the moment of capture means a later rewrite of your privacy policy cannot retroactively change what past visitors appear to have agreed to.
07 — TENANT ISOLATION
One key, one practice, no master override
Consential is multi-tenant: many practices, one platform. The failure mode that matters is a query that forgets to filter by practice and quietly returns somebody else's records. It is the most common serious bug in software of this shape, and it is almost always caused by the filter being the developer's responsibility on every single query.
So it is not. Tenant scoping is enforced in one data access layer that every query passes through, rather than sprinkled across route handlers where a single omission is invisible in code review. API keys are scoped to exactly one practice. There is no support key, no internal admin key and no "super key" that reads across practices — not because we trust our people less than other vendors do, but because a credential that can read every practice is a credential worth stealing, and the safest version of it is one that does not exist.
The isolation boundary is covered by tests that deliberately try to cross it: requesting another practice’s visitor record, reading another practice’s audit log, and passing a mismatched practice identifier to a configuration endpoint. Each is expected to return nothing or refuse outright, and each is asserted on every run — because a cross-tenant leak is not the kind of defect you want to find out about from a customer.
We treat a cross-tenant leak as our most severe class of bug. A single query that returns another practice's data is not a routine defect to be scheduled — it is an incident affecting every practice whose records were reachable, and it is handled as one, including notification.
08 — SUPPLY CHAIN
The widget loads on every page, so we treat it accordingly
We publish a JavaScript file that runs on every page of every client’s website. Any vendor in that position is asking for a great deal of trust, and the honest thing is to say how the file is protected rather than let you assume somebody has thought about it.
THE FILE ITSELF
Nothing else comes with it
A few kilobytes of vanilla JavaScript with no third-party dependencies. No framework, no analytics library, no package that could be compromised upstream and inherited by every practice site overnight. A dependency you do not have cannot be poisoned, and this is the clearest case in the product for having none.
THE ORIGIN
The bucket is not addressable
The storage behind the delivery network blocks all public access and is reachable only through the CDN’s own origin access control. There is no public URL for the origin to be attacked directly, and no plaintext path to it.
IF A BAD VERSION SHIPS
Rollback is one step
Previous versions are retained. Recovery is restoring the prior object and invalidating the cache — not rebuilding from a repository under pressure while client sites are serving the bad file.
WHAT IT CAN REACH
It only talks to us
The widget sends consent events to the Consential API over HTTPS and nowhere else. It does not phone home to a third party, does not load remote configuration from an unpinned host, and cannot be repointed by anything on the page it runs on.
It is also worth being precise about what the widget is doing on the page: it holds tracking scripts until a visitor has chosen, and releases them only for the categories that visitor permitted. That is why it has to load first and why it has to be small. It is the one piece of Consential that runs on your site rather than ours, and we would rather you judged it on how little it contains than on how much it promises.
09 — BACKUP & RECOVERY
Every backup is restored before we call it a backup
Backups that have never been restored are not backups, they are hopeful storage. The classic failure is not a missing backup job — it is a job that has been writing unreadable files for eleven months and telling nobody. So the verification is not an annual exercise here; it happens on every single run.
Each night the consent database is dumped, and the dump is then immediately read back and parsed to confirm it is a restorable archive. If it is not, the file is set aside as corrupt and the job exits with an error rather than overwriting good history with a broken file and reporting success. Backups are written with owner-only permissions into an owner-only directory.
| Objective | Target | What it means in practice |
|---|---|---|
| Recovery point | 24 hours | Worst case, a catastrophic host failure could lose up to a day of operational data |
| Recovery time | 4 hours | Target to rebuild the service and restore the database from the most recent verified dump |
| Backup integrity | Checked nightly | Not a target but a running control — an unreadable dump fails the job the night it happens |
Recovery point and recovery time are stated as objectives, which is what they are. We would rather publish a target we are working to than a guarantee we would have to defend in the middle of an outage.
10 — ACCESS & MONITORING
A short access list, and everything on it is logged
Production access is deliberately narrow. Every additional person who can reach a production system is an additional way in, so the list is kept to what the work actually requires, and it is reviewed rather than allowed to accumulate.
SERVER ACCESS
Keys, not passwords
- Password authentication disabled entirely
- Keys require a passphrase
- Direct root login disabled
- The application runs as a non-root user
- Access is removed the day someone no longer needs it
ACCOUNT ACCESS
Multi-factor, no standing keys
- Multi-factor on the root account, hardware key preferred
- Day-to-day work uses a limited account, also with multi-factor
- No root access keys exist to be stolen
- Administrative actions on the cloud account are logged
- Deploy credentials are scoped to what they deploy
APPLICATION ACCESS
Every request recorded
- Timestamp, method, path and practice on every call
- Source address, user agent, status and latency
- Kept as the operational record of who asked for what
- Infrastructure logging does not cover this, so we log it ourselves
Rate limits are enforced per endpoint and per key, sized to the job each one does. They are as much about containing a compromised credential as about stopping a stranger: a stolen key that starts pulling records hits a ceiling rather than draining an account at machine speed. We do not publish the thresholds, because the only party they would help is somebody calibrating to stay underneath them.
The failures worth alerting on are the quiet ones — a backup job exiting non-zero, a certificate approaching expiry, storage crossing a threshold, the API becoming unreachable. A control that stops working silently is worse than one that was never there, because it is still on the page you are reading.
11 — LIMITS
What we do not claim
A security page that only lists strengths is a sales page. These are the honest boundaries of what we are telling you, and we would rather you read them here than find them out later. Everything above this section describes controls that are running now; anything we intend to add but have not yet built appears below, dated.
We do not hold a SOC 2 Type II report
We have not completed a SOC 2 audit. It is planned as the platform matures, and when it is booked we will publish the date and the auditor rather than gesture at it. Any vendor implying an audit exists before it does is telling you something useful about how they handle the rest of their claims.
We have not published an independent penetration test
A third-party penetration test is scheduled work, not a completed one. Until the report exists and we can say who performed it and when, we will not describe the platform as "pen-tested."
Backups are verified nightly, but held on the same host
Every nightly dump is integrity-checked, and monthly archives are retained — but those files currently sit on the same host as the database they came from. That protects against data corruption and operator error; it does not protect against loss of the host itself. Off-host replication is committed work and this line will be updated with the date it lands, not quietly deleted.
Using Consential does not make your practice HIPAA compliant
There is no such thing as a HIPAA-certified product, and any vendor selling you one is selling you a misunderstanding. HIPAA compliance is a programme your practice runs across training, policies, agreements and systems. Consential is one component of the website layer of that programme, and it produces documentation you can point to. It does not discharge your obligations, and we are not a certification body.
We cannot see scripts your site adds after the page loads
The widget blocks tracking scripts present in the page as delivered. A script injected later by other client-side code can fall outside that control. Our scan cycle exists partly to catch exactly this, by comparing what actually loads on your site against what your consent configuration declares — but it is a detection, reported to you, not a guarantee of prevention.
We are not lawyers, and this page is not legal advice
Nothing here is a legal opinion about your obligations. Decisions about your privacy policy, your Business Associate Agreements and your regulatory posture belong with qualified healthcare counsel.
12 — DISCLOSURE
Found something? Tell us before you tell anyone else.
If you believe you have found a vulnerability in the widget, the API, the preference centre or the WordPress plugin, we want to hear from you directly. Email us with enough detail to reproduce it. We will acknowledge within two business days, keep you updated while we work, and we will not pursue legal action against anyone who reports a genuine issue in good faith and gives us reasonable time to fix it before disclosing publicly.
We do not run a paid bug bounty, and we would rather tell you that up front than have you discover it after doing the work.
SECURITY REPORTS
the contact form
Put “Security report” in the subject line. Include the affected component, reproduction steps and anything you observed. Please do not include real patient data in your report.
AGREEMENTS & DOCUMENTS
Privacy Hub
Business Associate Agreement, Data Processing Addendum, Privacy Policy, Terms of Service and our Risk Disclosure, together in one place.
See what is actually firing on your site right now
The scan is free and takes about a minute. It loads your site in a real browser and reports the trackers that run before any visitor has consented to anything.