Channekt › Security
Security
How a merchant's data is kept apart from every other merchant's, what is encrypted, what happens when something goes wrong, and the list of things that are not built yet. The last part is on the same page as the rest deliberately.
Last updated 30 August 2026Keeping merchants apart
This is the control the whole design is built around, so it is the one described in most detail.
Row-level security, forced, in the database. Twenty-five migrations declare forced row-level security on the tables that carry a merchant's data. Forced is the part that matters: without it the policy does not apply to the table's owner, so the application's own connection would bypass every rule it defines.
The organisation is set per transaction, not per connection, so a pooled connection handed to the next request carries nothing from the last one.
Queries cannot be written without a tenant. The repository base every query goes through takes the organisation as its mandatory first argument. There is no method that queries without one.
Every endpoint has an isolation test. Organisation A asks for organisation B's resource and receives a 404, not a 403, because a 403 confirms the resource exists. The suite is generated from the list of routes rather than written by hand, so a new endpoint with no test fails the build instead of passing silently. A deliberate leak is checked to fail that suite: the test that matters most is the one proving the tests would catch it.
The honest failure mode. A query that forgets its tenant scope against a forced row-level-security table returns zero rows rather than raising an error. That is the safe direction, because data cannot leak out, but it means a scoping mistake looks like "there is nothing here" rather than like a fault. It has cost us debugging time. It has not cost anyone data.
Who can see what
Access is by role, from one catalogue of permissions shared between the API and the interface. There are eight system roles and a test that asserts every cell of the matrix. A role without permission to read orders does not see an order: not in the interface, not in the API, and not in the service beneath both.
Permissions are enforced twice, at the route and again inside the service. A test proves the second one holds when the first is bypassed, because a route guard is a convenience and a service is the boundary.
Encryption
In transit: HTTPS everywhere, including between our own services.
At rest, whole database: provided by the host.
At rest, field by field: every credential we hold for somebody else's system - a shop's API credentials, the mail account, the assistant's key, and free-text notes kept against a connection - is sealed individually before it is written. Keys are derived per purpose from one master key held outside the database, so the key that opens channel credentials opens nothing else, and a copy of the database on its own yields no working token.
The consequence, stated plainly: encrypted fields cannot be searched, and a deployment that loses its master key loses their contents permanently. There is no recovery path, and that is not an oversight. A recovery path is a second way in.
Passwords are hashed with argon2id, and the parameters stored alongside each hash are re-checked on every sign-in so a hash written under weaker settings is upgraded rather than trusted. A new password is checked against Have I Been Pwned using the range API: the first five characters of its SHA-1 are sent and nothing else, so the password is never transmitted, not even hashed in full.
Sessions: access tokens are signed and live ten minutes. Refresh tokens are opaque and rotate on use, and re-use of a spent token revokes the whole family, on the assumption that a replayed token is a stolen one.
Logs pass through a redaction step that removes secret-shaped values by key name, by value shape, at any depth, and inside error cause chains. A test asserts that no secret reaches the output.
Who looked at what
Every read of a customer's personal data is recorded: who, when, from which address, and which records. Opening one order records that order; listing orders records the ones the list returned, so "did this member of staff look up this customer" has an answer rather than a count.
What is recorded is a pointer, never a second copy: an order id, not a name. A log that held the data would give an erasure request a second place to reach, and the newer one would be the one nobody remembered. A search term is never recorded, because it is the one field that is itself personal data - somebody looking a person up types their email address.
The record is kept twelve months and then the month is deleted outright. If the record cannot be written, the read fails: a system that says access is logged and then serves personal data without logging it is making a claim it does not keep.
Separately, an audit trail records what was changed rather than what was read: who did it, to which record, and what the values were before and after.
The infrastructure
The application, the database, the queue and the build pipeline run on Google Cloud in europe-west2, London. The database is PostgreSQL 17 on a private network address with no public route to it. Files - product pictures, imports, exports, the PDFs a parcel needs - are in Cloudflare R2 under the European Union jurisdiction. Cloudflare sits in front of the application for DNS and TLS. The full list of who is involved and what each one sees is on the sub-processors page.
Each service runs as its own identity with only the permissions it needs. Credentials are held in the platform's secret manager and reach the application as references rather than as values in a configuration file, and configuration is validated at start-up: a deployment missing a required secret refuses to boot rather than running degraded.
Backups
The database is backed up daily by the platform with point-in-time recovery, and those backups are in the same London region as the database. Restores are tested rather than assumed, and the procedure is written down.
What is not yet done is the drill: restoring into a fresh environment and measuring how long it takes end to end, and proving a merchant's data can be exported whole on request. Both are scheduled before the first production merchant, and neither has been performed yet. That sentence will change when they have been.
How changes reach production
Every change goes through the same pipeline. Type checking, linting, unit tests and the isolation suite run before a change can be pushed at all, enforced by a hook rather than by memory. A push to the main branch builds the images, runs the database migrations as a gate, and deploys only if the migrations succeed.
Secrets are scanned for on every commit. Dependencies are checked for known vulnerabilities. Nothing is deployed by hand from a laptop in the normal course of work.
If something goes wrong
This is our incident response policy. It is written down because the middle of an incident is the wrong time to decide who does what.
What counts as an incident
| Severity | What it means | Examples |
|---|---|---|
| Critical | Personal data has been, or may have been, exposed to somebody with no right to it | One merchant sees another's data; a database copy leaves our control; a credential store is opened |
| High | A control that protects data has failed, with no confirmed exposure | An authentication or permission bypass; a leaked credential with access to production |
| Medium | A weakness with no exposure and no failed control | A vulnerable dependency in a reachable path; a misconfiguration found before it was used |
| Low | Everything else worth recording | A report that turns out not to be exploitable |
The clock
It starts when any one of us becomes aware, not when the incident is confirmed and not when it began. From that moment:
| By when | What happens |
|---|---|
| 1 hour | Somebody owns it by name. Containment starts. A timeline document is opened and every action goes into it as it is taken |
| 24 hours | Every affected merchant is told, in writing, even if the assessment is not finished. The notice says what we know, what we do not yet know, and when the next update comes |
| 72 hours | A merchant who must notify a regulator has, from us, what that notice needs: the categories and approximate number of people and records, the likely consequences, and what we have done |
| 10 working days | A written review goes to every affected merchant: what happened, why, what has changed so it cannot happen the same way again |
What we do, in order
Contain first. Revoke the credential, disable the account, take the affected path out of service. A running exposure is stopped before it is understood, and the cost of an hour of downtime is smaller than the cost of an hour of exposure.
Preserve the evidence. Logs, access records and the audit trail for the period are copied somewhere they will not roll over, before anything is rebuilt. Nobody clears a log during an incident.
Establish who saw what. Every read of personal data is recorded (above), so the question is answerable from the record rather than estimated. That is what the access log is for, and an incident is when it earns its keep.
Notify. The merchant is the controller, so the merchant decides what goes to their customers and to their regulator; we do not contact a merchant's customers on their behalf unless they instruct us to in writing. We tell the merchant, we give them the facts their own notification needs, and we answer their questions in writing.
Fix, then review. The immediate fix, then the change that removes the class of problem, then a test that fails if it comes back. The review is written and it is honest about cause, including where the cause was a decision of ours.
Who to contact
During an incident, and to report one: support@channekt.com. It is monitored on working days and acknowledged within one working day.
Reporting a vulnerability
Send it to support@channekt.com with enough detail to reproduce it. We will acknowledge within one working day, tell you what we found, and tell you when it is fixed. We ask that you do not access, change or keep anybody else's data while demonstrating a problem, and that you give us a reasonable chance to fix it before publishing.
There is no bug bounty programme and we will not pretend otherwise. We will credit you if you would like us to.
What is not built
Written here rather than left to be found. A gap that was disclosed is a different conversation from a gap that was discovered.
Nothing alerts on a security event. There is a written incident procedure and access to personal data is recorded. What does not exist is anything that notices. The operational alerts watch queues, syncs and partitions; none of them watches access. A thousand order reads by one account at three in the morning is recorded and not flagged. The log makes an investigation possible; it does not start one.
Access logging covers orders, and orders are not everywhere personal data appears. Opening an order and listing orders are both recorded. The fulfilment screens - picking, packing, the documents a parcel needs - read the same shipping address and are not yet recorded.
Audit coverage is explicit, not automatic. A new endpoint that changes something gets no audit entry unless the person writing it adds one, and nothing fails if they forget. Coverage today is good; the guarantee is weaker than the coverage.
There is no second factor on sign-in yet. Passwords are hashed properly and checked against known breaches, sessions are short and rotate, but an account today is protected by one factor.
One shared database. Every organisation's data is in one PostgreSQL instance, separated by row-level security rather than by instance. The schema is partitionable by organisation, which is what makes a per-tenant or per-region split a migration rather than a rewrite, but today it is one database.
No external penetration test, and no certification. The application has not been tested by an outside firm. We hold no SOC 2 report and no ISO 27001 certificate, and we are not going to imply one by describing our controls in their vocabulary. An external test is scheduled before general availability.
Status. Channekt is under development. It has no production merchants and does not yet operate live channels. Everything above the last section is built and running; everything in it is not. Both statements are the reason this page exists.
Contact
AXICOM · support@channekt.com