Security

How the platform is protected, and what the first security review found

Encryption, access control and audit logging as they stand today, then the seven serious findings of our first independent security review, 23 September 2026, with what has been done about each. Where something is still open, it says open.

Last updated 27 September 2026. Written to be read, not to be survived.

Encryption

  • In transit: TLS on every connection: browser to platform, platform to database, platform to every supplier.
  • At rest: the database and file storage are encrypted at rest by Supabase on AWS; backups are encrypted in transit and at rest.
  • Sealed a second time in the application: national identity numbers, bank account numbers, and the connection tokens for a business’s WhatsApp, Google Calendar and payment processor are encrypted with AES-256-GCM under a master key held only by the running application, so a copy of the database on its own does not reveal them. Identity numbers are stored with a keyed fingerprint so a duplicate can be spotted without decrypting. Point-of-sale PINs are hashed with scrypt.
  • Not sealed a second time: customer names, phone numbers, addresses and order contents, which the database’s own encryption at rest covers. Said plainly because a client’s IT team will ask.

Access control

  • Sign-in is by emailed code or link; no shared passwords. A second factor for staff is not yet enforced (open, below).
  • Every request from a business is scoped to that business in the query; a live probe from one restaurant at six of another’s records answered not found on every one.
  • The browser’s public database key can read nothing: schema access is revoked from the anonymous and signed-in browser roles, and the server alone holds the key that can. Confirmed live in September 2026: zero grants to either role.
  • Staff hold capabilities granted by the owner, re-checked on every request. Viewing a client’s dashboard as the client is a signed, four-hour, audited session with a banner the client sees.
  • Secrets live in the hosting platform’s environment, never in code; the repository’s history has been checked for any.
  • Nine incoming webhooks verify their signatures over the raw body, constant-time, and fail closed.

Audit logging

  • Every sign-in, with the address it came from.
  • Every change to money (payout destinations, refunds, settlements), identity and settings, with who, when and what changed. Staff acting as a client are logged under their own name.
  • Not logged today: reads. A client cannot yet see a list of who opened its records; that is on the roadmap and is stated as open below.

The seven serious findings

The review read every route, every action and every migration, queried the live database and storage read-only, signed in as one client and pointed at another’s records, and ran a dependency audit. Nothing was actively exposing data. Seven findings were rated serious:

FindingWhat it wasWhat we did
S1
fixed
A restaurant could read another restaurant’s counter receipt by order idThe receipt lookup for a counter sale took an order id from the browser without checking which restaurant it belonged to; any signed-in restaurant holding another’s sale id could read the sale.Fixed 23 September 2026: the lookup now requires the restaurant with the id and checks it in the query. A live probe from one restaurant at another’s records answers not found.
S2
fixed
Driving licences were heading for a public storage bucketCourier documents were written to the same bucket as the public courier photo, whose visibility is fixed by whoever creates it first. Nothing had been uploaded, so nothing was exposed.Fixed 23 September 2026: documents have their own private bucket, and the code now refuses to reuse a bucket whose visibility does not match what was asked for.
S3
fixed
The public feedback form could make us call or email any number, without limitThe feedback form on a review card had no rate limit; a submission with a phone number queued a follow-up call. A loop of submissions with a victim’s number would have placed unwanted calls under a business’s name.Fixed 23 September 2026: five submissions an hour from one address, a hundred and twenty an hour in all, and one follow-up per phone number per day; over any of them the feedback is still saved and nothing is dialled or emailed.
S4
fixed
No backup had ever been restored, and point-in-time recovery was offDaily backups existed, kept seven days, but nobody had restored one and there was no point-in-time log.Done 23 September 2026: point-in-time recovery on, seven days; a restore rehearsed into a scratch project (schema, every table’s rows, counts compared: about three minutes), which found two things a real restore would have tripped on, both now written into the restore script.
S5
fixed
Sessions never expiredA signed-in device stayed signed in for ever.Done 23 September 2026: the console, restaurant, kitchen, point-of-sale, agent and partner apps sign out after a week without activity; the courier app after a month. A person signed out this way is told why.
S6
open
Sign-in code guessing was limited per address onlyVerifying an emailed sign-in code is limited to ten tries per fifteen minutes per network address, and the provider adds its own per-address limit; there is no counter per email address. Codes are eight digits and last an hour, which makes guessing slow, not impossible.Open. The fix is a limit of five tries per fifteen minutes per email address beside the per-address one, and the code lifetime down from an hour to ten minutes. It is next in the security queue; this page will say when it is in.
S7
fixed
The delivery code was four digits, plain, with unlimited triesThe code a customer gives a courier at the door decides whether the courier is paid; it could be tried ten thousand times from the courier app.Fixed 23 September 2026: the fifth wrong code locks the order, rider operations are rung, and a person must confirm the delivery; every code is now generated with a cryptographic random source.

What is still open

  • S6, the per-email limit on sign-in code guessing (above).
  • A second factor for staff sign-in. Time-based one-time codes are enabled at the identity provider but not yet enforced or wired into the app; the owner’s own database console account does not yet have a second factor on.
  • Row-level security is enabled on 81 of 194 tables with no policies; the application, not the database, is what separates one client from another today.
  • Read logging. Reads of a client’s records are not written to the audit log.
  • Self-serve deletion of a whole client. Deletion cascades in the schema and is done by us on request; no client can do it from the dashboard yet.
  • A penetration test. The September review was a code and configuration review by an engineer with full access, not an external penetration test.
  • One known-vulnerable dependency (the spreadsheet library) in a non-public path, tracked for replacement.

When something goes wrong

If we learn of unauthorised access to a client’s data, or reasonably suspect it:

  1. Contain: revoke the access (a key, a session, a capability) within the hour of learning.
  2. Tell the client within 72 hours, in plain words: what happened, whose data, what we know and do not know yet, and what we have done. Sooner where a person could be harmed.
  3. Help the client meet its own duties: regulators and affected people are the controller’s to notify; we give them everything they need to do it.
  4. Write it up within two weeks: cause, timeline, what changed so it cannot repeat, published here in summary.

The owner is the incident contact; a second named person is added as the team grows.

SOC 2

Anty Virtual does not hold SOC 2 today, and we will not imply otherwise. What we do instead: we had our first independent security review in September 2026 and publish its findings and their status on our Security page; data is encrypted in transit and at rest, with identity numbers, bank accounts and connection tokens sealed a second time in the application; every sign-in and every change to money, identity and settings is logged; backups are kept with seven days of point-in-time recovery and a restore has been rehearsed; every sub-processor is under a data-processing agreement, listed here, with fourteen days’ notice before a new one. We will begin a SOC 2 Type I readiness assessment when a client contract requires it, and we will say here when that starts.

Reporting a vulnerability

Write to security@antyvirtual.com. We answer within two working days, fix what is real, and credit you if you want it. Please do not access, change or keep anyone else’s data while showing us a problem.