Security
I watch your application from the outside, hold as little as I can get away with, and throw most of it away after 30 days.
Where the infrastructure is
Core infrastructure is hosted in the European Union, and not as a setting somebody could change without noticing: it is where the machines are.
- Servers and database
- Hetzner, in Germany. Nothing of yours is hosted anywhere else.
- Storage
- Screenshots, video and traces, in EU object storage, on private buckets reached through one-off URLs rather than with a key.
- Sent through Seznam.cz, in the Czech Republic.
- Backups
- In a separate private bucket on the same EU infrastructure, with separate credentials, encrypted with a key that the rest of the product never holds.
Two things can leave the EU and I would rather name them than let the paragraph above cover them. If you sign in with GitHub, your email address, name and avatar reach GitHub in the United States; sign in with a link instead and they never do. And an AI request is routed as the next section says.
How AI requests are routed
This is a different question with a different answer, so it gets its own heading rather than a clause in the one above.
I use AI in two places only: proposing checks when you add a project, and locating an element as a last resort after deterministic methods fail. For the first I send the server-rendered HTML of your home page and the links on it. For the second I send the screenshot and the page's accessibility structure and ask where the element went. Nothing else in the product asks a model anything.
I restrict AI inference to the EU regional endpoints pinned below, today Azure in the EU. OpenRouter is the routing layer and is a United States company, so I don't describe the complete AI path as wholly EU-resident: the request passes through OpenRouter on its way to the pinned provider, with logging switched off on that account.
Right now every pinned provider is an EU regional endpoint, so inference for both of those calls happens inside the EU. That is a property of the list below rather than a promise: pin a provider without an EU region and inference leaves the EU, and this page will say so instead.
Pinned right now
azure/eu- Microsoft Azure, Azure (EU) regional endpoint operated by Microsoft — an EU region
What holds the model in check
- Last resort, and optional
- It is only asked after every deterministic way of finding the element has failed, and you can opt out of it for your account by writing to the support address at the bottom of this page; the step then fails instead.
- Never with a secret
- A run that carries a credential or an email step is never offered to it, so nothing behind a sign-in is ever sent to a model.
- Never the judge
- A model never decides whether your application is broken, never creates a finding, and never reads the visitor-error channel. Every verdict is deterministic.
- Fail closed
- Every request denies data collection, requires a zero-retention endpoint, allows no fallback to another provider, and fails rather than route elsewhere. Automatic routing is never used.
- Prompt logging off
- Prompt and completion logging is switched off in the OpenRouter account privacy settings; verified by the operator on 2026-09-01
I hold no individual written zero-retention letter from OpenRouter or from the inference provider, and I don't claim one. What I rely on is the request-level instructions above, the logging switch on the account, and the providers' published retention and training commitments, which I re-read before each deployment. If the pinned provider ever stops meeting them, that AI path stops until a compliant provider is configured; it never falls back to a provider that retains. The whole chain, including who built each model, who routes the request and who runs it, is on subprocessors.
How each run is boxed in
Each check runs in an isolated browser container. It has no access to my database, my storage credentials or my AI credentials. The container is created for one run and destroyed after it, with a read-only filesystem, no extra privileges, a memory limit and a hard timeout, because it opens pages I do not control and some of them will one day be hostile.
It cannot reach my internal network. It fetches its job through a signed channel, writes its screenshots and video through one-off URLs issued for that run, and everything it sends back is treated as untrusted input and validated before anything is stored. The one thing it does hold, for one run, is the sign-in values that run needs, and the next section says how.
Your application credentials
Stored application credentials are encrypted at rest and opened only inside the isolated runtime that needs them. The master key never goes to a runner: a runner receives the ciphertext and the data key of the values the current run references, opens them in memory, and loses them when the container is destroyed. This is envelope encryption with a narrow per-run key, and I don't call it zero-knowledge encryption, because the control plane holds the master key and that phrase would mean it does not.
Decrypted values are never written to a log, an error report or an alarm. A run that carries a credential or an email step records no video and no network trace at all, is never offered to the visual AI fallback, and sign-in fields are blanked out of any step screenshot I take. I recommend a dedicated account in your application for these checks, with the narrowest permissions that let the check run.
What I keep, and for how long
I keep recordings for 30 days and no longer. They can contain your users' names and email addresses, and I'd rather not hold that longer than I need to.
That cap is the same on every plan, including the one that keeps a year of history. History is the record of what happened; recordings are the video of it, and a run older than 30 days says the recording is gone rather than showing an empty player. Raw visitor-error events go after two weeks, a run started from the landing page without an account within 30 minutes, and a recorder session with its element crops within 24 hours. The privacy notice has the full table.
Backups and an outside check
The database is backed up daily. Backups are encrypted separately, with a key that only the backup process holds and this website never sees, retained for 30 days, and restoration into an empty database is verified automatically every month. An upload that was never restored is not counted as a backup: only a proven restore is. A production deployment refuses to start without that key, so there is no production configuration of this product that runs without backups.
An outside service asks every five minutes whether this site answers and publishes the answer on a public status page. It sees this site's health endpoint and nothing of yours.
What I never get
Your database. Outside checks need nothing installed, no agent on your servers and no connection into your infrastructure. I read your application the way one of your visitors does, over the public internet.
Network traces. I record them for debugging and I never offer them to you for download, because they can hold tokens and other people's personal data.
Your address, on a session. The IP address and browser identification of a sign-in are deliberately not stored. A run started without an account keeps a keyed hash of the visitor's address, used only for a rate limit, and never the address itself.
Your visitors' identity. The optional error snippet sets no cookie, creates no identifier, reads no storage and sends no form value; the query string and fragment are cut before anything leaves the browser, and I store neither the visitor's IP address nor the full User-Agent. An error message or a URL path can still carry something your application put there, and I don't pretend otherwise.
Logging, and what I don't claim
I don't log decrypted application credentials, session tokens or secret field values. Links that carry a token, the sign-in link, the ones in an alert email, the one your deploy calls, are stripped out of the access log before it is written. That is a rule about what is written on purpose, not a guarantee that no sensitive value can ever appear anywhere.
I hold no SOC 2 report, no ISO 27001 certificate, no HIPAA attestation and no PCI certification of my own; card details are entered on Stripe's pages and never reach me. I am not "GDPR certified", because no such certificate exists, and the subscription includes no contractual uptime percentage or service credits. What is on this page is what I actually do, and it is what you may rely on.
Signing in
A link in an email, or GitHub. There is no password to lose. The session lasts seven days and is refreshed while you use the product, and the connection tokens GitHub gives me are encrypted at rest with a key that lives in the environment rather than in the database.
Telling me about a problem
If you find a security issue in stillworks.watch, email security@stillworks.watch. Include enough detail to reproduce it and avoid accessing or modifying data that isn't yours. I review reports as quickly as I reasonably can; there is no bug bounty and no promised response time for security reports. Traffic from me landing somewhere it should not goes to the abuse address and gets an answer from a person within 24 hours. Anything about your own account, including opting out of the visual fallback, goes to support@stillworks.watch.