Security

Security

Last updated: September 20, 2026

Tada reads your repository, drives your app in a browser, and posts a verdict on your commit. This page says what that touches. Every claim here was checked against the code that does it.

What Tada reads on GitHub

Tada connects through a GitHub App you install on the repositories you pick. It holds four permissions: Contents read, Metadata read, Deployments read, and Checks write. A fifth, Issues write, is used only when you ask Tada to open an issue.

  • Your flows, at the commit that deployed. Tada reads .tada/flows at the exact SHA GitHub just published, so a run replays the flows that shipped with the code.
  • Enough source to map your screens. Tada lists the files at that commit and reads the ones that describe pages and routes.
  • Never a secret-shaped file. The reader refuses .env files, private keys, certificates, keystores, and any path named secret or credential. It refuses them by name, before the file is fetched, because the contents are the thing not to look at.
  • Deployments. GitHub tells Tada when a deployment succeeded and hands over the address it published. That event is what starts a run.
  • Branch names and the repositories the installation covers, so the dashboard can offer them instead of asking you to type them.

Tada does not read your pull requests. The App subscribes to installation and deployment events only, and holds no pull request permission. Every webhook is rejected unless it carries GitHub's signature over the exact bytes sent.

What Tada writes on GitHub

  • One check run per verdict, on the commit that was deployed. Passed, blocked, or neutral when Tada itself failed. Our outage never files a failure against your code.
  • An issue, when you ask for one. Rivet opens it with the title and body she showed you first.

Nothing else. Tada pushes no code, opens no pull requests, and creates no branches.

Where runs execute

Every run executes on Tada's hosted runner. Your machine launches no browser, records no frames and encodes no video. The Tada tool on your machine submits the run and waits for the link. Releases 0.1.31 to 0.1.38 still recorded locally and are frozen; 0.1.39 and later do not.

A run replays your flow files step by step. No AI model decides whether a step passed, and no model is in the loop while a run executes.

When the app you are checking runs on your laptop

Our runner cannot reach your localhost, so the Tada tool opens a Cloudflare tunnel to it for the length of the run.

  • It opens before the run is submitted and closes as soon as the verdict lands.
  • The tunnel is named, it lives in Tada's own Cloudflare zone, and its token reaches that one tunnel and nothing else.
  • If the tool is killed before it can close the door, our server closes it within twenty minutes.
  • Treat the address as unlisted, not secret. It is random and unpublished, and anyone holding it can reach your app while the run lasts. That is the same exposure as a preview deployment.

The login Tada signs in with

Tada generates a password for a throwaway test user, and your own seed creates that user in each environment. A disposable login for a disposable database is the default, by design.

  • Stored encrypted, with AES-256-GCM. The key lives in our secrets manager and never in the database, so a database dump on its own is inert.
  • A flow never holds the value. It types {{secret.TADA_TEST_PASSWORD}}, and the runner substitutes the real value at the moment of typing. The password reaches no caller, no saved flow, and no commit.
  • Reading one back needs a signed-in session in the dashboard. An API key cannot read it, and replacing an account is the owner's to do.
  • A real sign-in you save is sealed the same way, and those routes refuse an API key outright.
  • When a login stops working, Tada tells you first. A run that finds a dead session marks it expired, and a nightly check signs in the way a pull request's run would. When it cannot get in, it asks you in the app instead of failing your next merge.

What a run records

A run keeps its verdict, the steps it took, the frames it captured, and what your app answered while it drove.

  • Of the requests your app answered, a run keeps the method, the path, the status, the step that sent it, and the second it answered.
  • The query string is dropped whole, because a bare token in a URL is a credential and no rule tells one from a harmless name.
  • No request bodies, no response bodies, no headers, no cookies.
  • Only your own site is watched. A 500 from an analytics host is not a finding about your app.

What we keep, and for how long

Videos, poster images and the stills from a blocked run are stored in Cloudflare R2 under your workspace's own prefix. Nothing in that bucket is public: playback is a signed URL that expires in an hour, and the API signs a fresh one per request.

  • On a paid plan we keep your demos indefinitely. On the free plan we may stop serving a demo after 30 days.
  • Nothing is removed on a timer today. Files go when you delete a demo, and everything goes when you delete your account.
  • Runs, flows and environments belong to the repository connection. Remove the connection and they go with it.

Who can open a demo link

A /d/ link is public to anyone holding it. There is no sign-in and no account. Treat a share link as unlisted, not secret, and anyone watching can leave a comment under a name they type.

A run that did not pass has no video to leak. The encode never happens, the link answers 404 until a recording is ready, and the stills that show what broke stay inside your workspace.

How to revoke

  • Uninstall the GitHub App from your repository settings on GitHub. Tada deletes the installation and its repositories, and the flows, runs, environments and stored logins hanging off them go with them.
  • Disconnect Slack or a deployment provider in Settings, and the stored token for it is deleted.
  • Delete your account in Settings. Billing is cancelled first, then the files, then your voice clone, then the rows, then the login itself. The order is chosen so an interrupted deletion can be finished rather than leaving bytes nobody can find.

Your demos are not attached to a repository, so an uninstall leaves them standing. Delete the demo, or delete the account.

Where your data goes

Tada is hosted in the United States. These are the companies that process something on our behalf, and each one is in the product today:

  • Railway: hosts our API, our site and our runners.
  • Supabase: our database and sign-in.
  • Cloudflare: stores videos and stills, and provides the temporary tunnels.
  • GitHub: the repository connection, the flow files and the check.
  • Stripe: payments. We never receive your card number.
  • Loops: the emails we send you.
  • PostHog: product analytics and error reports.
  • OpenRouter, and the providers it routes to: Anthropic, OpenAI, Moonshot AI, xAI and DeepSeek. This is Rivet and the weekly summary, never a run.
  • ElevenLabs: narration, and your voice clone if you record one.
  • An outside service that reads the words on each screen while Tada learns your app. It gets labels and short visible lines, never page code, cookies or screenshots. Ask us and we will tell you which company it is.
  • Slack: only if you connect it, and only to post where you chose.

The Privacy Policy says what each one receives.

What we do not have yet

Said plainly, so a review does not have to ask twice.

  • No SOC 2 report and no ISO 27001 certification.
  • No third-party penetration test.
  • No SAML single sign-on for your Tada account, and no audit log you can export.
  • Free plan retention is a policy rather than a mechanism today. No job expires a demo, so a free demo stays reachable until you remove it.

If your review needs one of these, tell us which and when. We would rather say no today than imply a yes.

Reporting a vulnerability

Email [email protected] with what you found and how to reproduce it. We will reply, and we will tell you what we changed.