Security overview

How the service is built, what protects it, and what we do not have. Every claim here can be checked against a response header or against the behaviour it describes.

Last updated 18 August 2026.

What this page is

A description of how the service is put together, written for somebody deciding whether to depend on it. It is not a certification and there is no certification behind it. Where a control exists, this page says what it is and how it can be checked. Where one does not, it says so rather than leaving the shape of an answer.

An external audit of this service was carried out in August 2026. The changes dated 18 August 2026 to the privacy notice, the cookie notice and the list of suppliers came out of it.

How the service is put together

Three separate deployments, and the separation is the main structural decision in the whole product.

  • The marketing site at qrthatlasts.com. No database, no accounts, nothing to attack.
  • The application at app.qrthatlasts.com. Where accounts, codes and artwork live.
  • The redirect at elysiu.me. One small function that resolves printed codes, in its own repository, sharing no code with the application.

The third one exists in isolation on purpose. A printed poster has to outlive the dashboard it was made in, so the thing that resolves it does not share a build, a dependency tree or a failure mode with the product. If the application is down, printed codes still resolve. That is a property of the architecture rather than a promise about our uptime.

The redirect never caches a response, which is what keeps the product promise true: change a destination and the next scan follows it. Every response from it carries no-store and a noindex instruction, so nothing it serves is retained by an intermediary or listed by a search engine.

Where data is held

One database, run by Neon on Amazon Web Services in their London region. Accounts, codes, destinations, scan records and the safety record are all in it and nowhere else. Connections to it are made over HTTPS.

There is a second database used for testing, which is a branch of the first taken as a copy. Every automated check runs against that branch rather than against customer data, and the script that picks between them refuses to run if the two are ever pointed at the same place.

The list of suppliers names everyone else who handles anything, and what.

Getting into an account

  • Passwords are stored as a one-way hash. We cannot read yours and nobody here can recover it, so the only route back into a locked account is a reset.
  • Two-factor authentication is available to everybody, using an authenticator app. The secret and the backup codes are stored encrypted.
  • Repeated wrong passwords lock the account for a rising period rather than a fixed one. Five failures cost a minute, seven cost five minutes, nine cost half an hour and eleven cost two hours. The count resets after half an hour without another failure, and the records behind it are deleted within a day.
  • Signing up, requesting a reset and reporting a code are each limited per address as well as per account, because a per-account limit is defeated by making more accounts. The address is held as the same daily-salted hash used everywhere else, and those counters are deleted after two days.
  • There is one sign-in screen for everybody. A separate door for staff would be one more thing to find and attack, and its existence would advertise that staff accounts exist.

Staff access

A small number of people can see customer accounts, and what they can do is bounded and recorded.

  • The staff console is not reachable by anyone who is not staff. It answers with a not-found page rather than a redirect, because a redirect would confirm it exists.
  • Staff cannot open it without a second factor, whatever their password.
  • The check lives in the layout, so every screen added beneath it is protected the moment it is created, and each action re-checks independently rather than trusting the screen it was launched from.
  • Every staff action is written to an audit trail with who did it, what they did and when. So is the customer side: an account being created, an address confirmed, a plan starting, changing or ending. That trail is never deleted, and it is keyed on the account rather than on a person.
  • Nobody here can read your password, and nobody can delete a destination’s history. Both are refused by the database rather than by a rule somebody follows.

What protects the browser

Every response from all three sites carries a content security policy, and anyone can fetch the headers and read it. The marketing site allows scripts from its own address and from nowhere else, which is why no third-party script can be added to it by accident. The application allows one more, which is Paddle’s checkout, named explicitly rather than the policy being loosened.

  • HTTPS everywhere, with a browser instruction to refuse plain HTTP for this domain and its subdomains for a year.
  • No page may be framed by another site, which is what a clickjacking attempt against a code generator would need.
  • Content types are not guessed by the browser, referrers are trimmed when leaving the site, and camera, microphone and location are turned off at the page level so a future dependency cannot start using them quietly.

Where codes point, and abuse

We run a redirect on a domain that a growing number of already-printed codes point at. If that domain is blocklisted, every one of those codes dies at once, including posters on walls that cannot be recalled. Most of what follows exists because of that asymmetry.

  • Every destination is checked when it is set, and again on a schedule afterwards. Checking only at creation guarantees nothing here, because the whole point of a dynamic code is that the destination changes after printing.
  • The verdict is read at redirect time, not merely recorded. A destination that failed its check is not served, however long ago it was printed.
  • Other link shorteners are refused as destinations, because chaining one hides the real address from every check.
  • Anyone can report a code without an account, from a link on the pages the redirect itself serves. Somebody who has just been sent somewhere nasty will not create an account to tell us.
  • A whole account can be suspended, not only a single code, because one signup minting two hundred codes is faster than blocking them one at a time.

Keeping customers apart

Every query that reads a code, a destination or a scan is scoped to the account it belongs to. That is not left to review: one of the automated checks creates two accounts and tries, from each, to read and to change the other’s codes, destinations and scan data. It runs before every release and the release does not happen if it fails.

What runs before anything is released

Deploys are manual and each one runs more than thirty separate gates first, covering safety, artwork export, the download guard, scans, abuse limits, billing, account lifecycle, erasure and the isolation check above. A deploy either passes them or does not happen.

  • Database changes run inside the build, before the code that depends on them is live. This exists because the opposite once shipped and took down every sign-in, correct passwords included.
  • Every artwork file is decoded before it is handed over. If the generated file does not scan, the download is refused with a plain reason. The characteristic failure of this product is artwork that looks right in the app and is wrong in the file, discovered after the print run.
  • One of the gates seeds an account, puts its email address into every place the application can put one, erases it, and then reads every text column in the database looking for it. That check is deliberately not written against a list of known columns, because a list can only fail to mention the column somebody adds next year.

Monitoring

A watchdog runs every four minutes. It scans a real code on the real domain over the public internet rather than asking the application whether it is well, and it separately checks that the application answers, that the database answers, that the nightly job is still creating the partitions the next months of scans will need, and that server errors have not risen above a set rate in the last hour. Failures are emailed. There is no public status page yet and no uptime figure worth publishing, and this page will not carry one until there is a real record behind it.

Secrets

Credentials live in the hosting platform’s environment and are not in the repository. The value that protects the scan pipeline is one of them, and its absence is reported loudly at startup rather than silently producing a service that quietly stops distinguishing one scanner from another.

What we do not have

Stated plainly, because a buyer will ask and because the absence of a claim is easier to trust than a vague one.

  • No ISO 27001, no SOC 2, and no independent penetration test to publish.
  • No published uptime figure and no service level agreement. There is monitoring, and not yet enough accumulated record to make a number mean anything.
  • No tested recovery time or recovery point. The database platform keeps its own history, and until somebody has actually restored from it and timed the result there is no figure here worth quoting.
  • No single sign-on, and no customer-facing audit log. Both are wanted and neither is built.
  • A small team, which means a small number of people with production access and no separation of duties between them. That is the honest shape of a company this size, and it is better said here than found out later.

Reporting a security problem

Write to hello@qrthatlasts.com with enough detail to reproduce it. We will confirm we have it, tell you what we found, and tell you when it is fixed. We will not threaten anybody who reports a problem in good faith and does not take, change or publish other people’s data while finding it.

If the problem is that a code on our domain is pointing somewhere harmful, that has its own route and it is faster: every page the redirect serves for a code that failed a check carries a link to report it, and no account is needed.

Who you are dealing with

Company
NUVIX STUDIO LIMITED
Registered in England and Wales, number
16287603
Registered office
82a James Carter Road, Mildenhall, United Kingdom, IP28 7DE