sixty

always-on performance monitoring for apps you vibe-coded

Sixty seconds to install. Sixty seconds to an answer.

You shipped a change. Something got slower, or a button stopped working, and nothing in your logs says why. Sixty is watching — every release, every day, whether or not you are — and it tells you in plain English which change did it. Then it hands the evidence to the coding agent you already have open, which fixes it.

1
command to install
0
dashboards to build
0
thresholds to tune
0
user data collected

You ship it. We catch it. Your agent fixes it.

There is no dashboard to check and no alert to configure. The three things below are the entire product, and you only do the first one.

  1. You ship a change

    It was fine on your laptop, against a small table, and the tests passed. Nothing about it looks dangerous — which is the problem.

  2. We see the shape change

    This release is compared against the last one. One query per render became fourteen. You configured no threshold, because there is none.

  3. Your agent fixes it

    The evidence goes over MCP to the coding agent you already have open — numbers, query, stack frames — and it writes the fix and records what it did.

Then you ship again — and that release becomes the baseline this one is measured against.

The point is that it never stops.

Every release gets compared to the one before it, a couple of minutes after it goes out, for as long as the app is running. You do not start it, you are not asked anything, and there is no run to remember to do.

watchingcheckout-api · production
  1. 4c02f1ano drift · 47 operations compared2 min
  2. bb7e340no drift · 47 operations compared3 min
  3. 2e9b503returns nothingprofile.load12 rows → 0 rows4 min
  4. 5a1d0c8no drift · 48 operations compared2 min
  5. c40aa19no drift · 48 operations compared2 min
  6. 9d3f01erowsorders.getUserOrders30 rows → 30,000 rows4 min
  7. 9d3f01eN+1orders.enrichOrders1 query → 14 queries4 min
  8. 7b21ee4no drift · 46 operations compared3 min
  9. a3f9c21no drift · 46 operations compared2 min
  10. d81b904no drift · 46 operations compared2 min
Seven of these ten deploys changed nothing, which is the normal week and the reason this is not a thing you remember to do yourself: check after every release and the answer is “fine” six times running, until it is not. Reconstructed from a pnpm e2e run — those are its numbers — with illustrative revisions.

Works with whatever you already have open.

Sixty does the watching; the coding agent you already use does the fixing. The handover between them is one MCP server, which is why the install is the same two values everywhere — and why building somewhere with no editor of your own is a prompt pasted into a chat instead.

Claude Code

One command. The findings arrive as a payload it can act on.

Cursor

The same two values as a config block.

Lovable

No editor of your own — paste one prompt into its chat.

Replit

Agent and deploys in one place; the release marker comes free.

VS Code

Copilot, Continue, or whatever else you have wired in.

Codex

And Windsurf, Zed, Cline — anything that speaks MCP.

Anything that speaks MCP works, including tools that did not exist when this was written. Nothing here needs a plugin from us, and nothing here is a partnership — it is one open protocol doing what it was for.

It didn't get slower. It changed shape.

This is why regressions reach your users. A query that returned 30 rows now returns 30,000 — on your laptop, against a small table, that barely moves the clock. It looks fine. Then real data arrives and it takes the app down.

Speed is a symptom and it shows up late. Shape is the cause and it shows up immediately, so that is what gets measured: rows returned per call, database calls per render, bytes per response.

no change10×100×1000×rows returned per call30 30,000database calls per render1 14response time, p9537ms 1.94s
One deploy. Nothing in the code looked dangerous, and every one of these was caught before a user complained — this is the output of pnpm e2e, which ships a deliberate regression and detects it, reproducible from a clean clone.
N+1

14 queries where there was 1

The N+1 born inside a render. Each query is fast, so nothing looks wrong per request — only the count is wrong, and nothing else you can install will tell you.

rows

30,000 rows to show 12

A query with no limit, or one that reads the whole table so your code can filter it afterwards. Fine on day one, fatal on day ninety.

page speed

A page that's slow only in Brazil

Real page timings per route — largest paint, interaction response, layout shift — broken down by country, so you find out it's a network problem and not your code.

loop

An effect that never stops

A useEffect that sets state it also depends on, re-running forever. We catch it in two places: in your code before it ships, and in production as one endpoint suddenly called 300 times where it used to be called once.

Errors, including the ones that report success.

A crash is the easy case. The expensive ones return 200, log nothing, and leave somebody staring at a screen that does not work.

GET /orders200 OK

No orders yet

status
200
exceptions
0
response time
84ms
rows returned
12 → 0
Nothing threw, nothing was slow, and every check passed. The query still ran and came back empty — which is what a permission rule that is missing or wrong looks like from the outside, and why the shape of the answer is the thing worth watching rather than whether there was one.
browser error

The white screen

Client-side errors grouped by where in your code they happen, not by their message — so one bug is one finding instead of two hundred.

does nothing

A button with no handler

Nothing throws, nothing is slow, the user clicks and leaves. Found by reading your code at build time, ranked by how much traffic the page actually gets.

returns nothing

It loads. It's empty.

A query that still succeeds and now returns no rows — the most common way one of these apps breaks, and the hardest to see.

access refused

Saved. Except it didn't.

A 401 or a 403 is not an error anything usually counts. The server declined and the person saw a form that submitted and did nothing.

Your users can see each other's data. Nothing errors.

A missing row-level security policy does not throw, does not get slower, and does not produce a support ticket from the person whose data leaked. It produces a page that works, for everybody, showing everything.

select * from orders where user_id = $1

24 rows returned — every customer's8 rows returned — only theirs

The database applied no policy and raised no error, because it was not asked to. Sixty reads the statement your app actually sent — normalized, with the values already stripped — and the thing it looks for is a per-user table with no user_id, org_id or auth.uid() anywhere in it, handing back more rows than a lookup would.
missing tenancy

A query that never says which user

Three conditions have to hold at once before it says a word — a table that is per-user by name, no scoping condition anywhere in the statement, and enough rows coming back to rule out a lookup. Telling you your users can see each other's data is a serious thing to get wrong, so it asks you to confirm rather than asserting a leak.

exposed key

A key that should never have shipped

A credential written into code the browser downloads. We report which kind it is and where, never the value. Your Supabase anon key is deliberately not flagged: it is meant to be public. The one that bypasses every permission rule in your database is not, and they look almost identical.

access refused

A wall of 401s that nobody is counting

Requests turned away at a rate they were not turned away at last release. Either your permissions changed and something legitimate is now locked out, or something is trying doors — and both are worth a look on the day rather than in a log nobody reads.

Postgres says exactly what went wrong. We read it.

A failed query is not one thing. Permission denied, a column that does not exist, and a duplicate email are three different problems with three different fixes, and every one of them arrives as a red line in a console nobody has open. The database distinguishes them by code. So do we.

Which means the finding your agent receives is not “this query failed”. It is the name of the defect, and that is the difference between an agent guessing and an agent fixing.

42501

It's your row-level security

Not inferred from a 403 — quoted. Postgres returns 42501 when a policy refuses a statement, so the finding says a policy refused it, on which table, and how often. The most common way a published app locks out the people it was built for.

schema drift

Your code asks for a column that isn't there

The migration was described in a chat and the frontend was written against the description. Postgres answers 42703, PostgREST answers PGRST204, and both mean the same thing: the code and the schema have come apart. This is the defect that generated apps have most often and that no amount of timing will find.

token refresh

Everybody gets logged out, over the next hour

The background renewal that runs on a timer in every open tab is measured apart from somebody signing in, because they are not the same event. A sign-in failing is a support ticket. A refresh failing is every session in the world ending, quietly, as each one comes up for renewal.

realtime

Every message arrives nine times

A subscription opened in an effect with no teardown re-subscribes on every render. Nothing throws, nothing is slow, and the user watches each new message appear nine times while your connection count climbs toward the limit that will turn the feature off for everyone.

storage

Uploads, by bucket

Failures and sizes per bucket — never per file, because in an app like this the filename is very often the user's own. Policies and limits are set per bucket anyway, so the bucket is the thing you can actually act on.

edge function

A 401 that isn't your fault

When the platform's gateway and the token your client holds disagree about a signing algorithm, every authenticated call to every edge function is refused. It looks exactly like your code being wrong. It is worth knowing within the hour that it isn't.

What it runs on

The browser half works with any backend, in any language — it watches the page, not your server. The server half is Node and Postgres today.

Working today

  • Node.js
  • TypeScript
  • PostgreSQL
  • React
  • Next.js
  • Vite
  • Remix
  • React Router
  • SvelteKit
  • Supabase
  • Lovable
  • Any front end

Not yet

  • Python
  • Ruby
  • Go
  • PHP
  • Java
  • Rust
  • MySQL
  • SQLite
  • MongoDB

The right-hand column is a plan, not a promise, and it is ordered by what people ask for rather than by what is easiest. If yours is on it, the front-end agent already works for you today — and telling us which one moves it up.

From €14.99 a month.

Two plans, and both of them are the whole product — every finding, the agent API, and the MCP server. The only difference is how many apps you point it at. Both start with a 7-day trial: a card up front, and nothing charged if you cancel before it ends.

Solo

€14.99/month

1 app

The full product for a single app — all findings, the agent API, and the MCP server. Staging and preview deploys of the same app do not count as extra.

Studio

€49.99/month

5 apps

The same product across five apps — every finding, the agent API, and the MCP server. Staging and preview deploys of the same app do not count as extra, so five apps is five things you ship rather than five environments.

Staging and preview deploys of the same app are the same app — a one-app plan is not exceeded by instrumenting your staging environment, which is the thing we would most like you to do.

Already paying for something else? How this compares to Datadog, New Relic and Sentry — including the five kinds of team it is the wrong tool for.

Sixty seconds, honestly

You don't install this. Your coding agent does — and then it fixes what we find, in the same conversation. One command:

claude mcp add sixty \
  -e SIXTY_API_KEY=sixty_sk_… \
  -e SIXTY_ENDPOINT=https://ingest.sixty.sh \
  -- npx -y @sixty-sh/mcp

Cursor, VS Code, Codex and anything else that speaks MCP take the same two values as a config block. Building on Lovable, with no editor of your own? Paste one prompt into its chat instead — sign in and it is written for you with your key already in it.

  1. 1

    Say: install sixty in this project

    It reads your repository, works out whether that means a Next config, a Vite plugin or a start command, and wires the agent in. Nobody has to know which shape this project is — the thing doing it can see.

  2. 2

    Say: fix the worst finding

    It pulls the evidence — the exact numbers, every stack frame, the query, and which child calls account for the change — writes the change, and records what it did on the finding so you can read it later.

  3. 3

    Publish twice

    Comparison is anchored to deploys, so the first finding arrives after your next one. The release marker comes from Vercel, Render, Railway, Fly or GitHub Actions on its own.

That last step is the honest catch. Install takes a minute; the first comparison takes as long as your next deploy, because there is nothing to compare a single release against.

It never learns anything about your users

No session IDs. No user IDs. No cookies. No recorded clicks or replays. No text from your pages, and no URLs — every address is reduced to its shape (/orders/:id) inside the browser, before anything is sent.

Query values never leave your app either: where email = 'alice@…' becomes where email = ? in your own process. What we receive is counts and distributions.

Which means there is something we genuinely cannot do: tell you who hit a bug. We can tell you what broke and how often. That is a deliberate trade, not a gap — and it is why installing this adds nothing to your consent banner. (We run analytics on this page, though, because we would like to know if any of this worked. Ours is at the bottom. It has a cartoon on it and a Reject that rejects.)

Find out what your last change did.

Sixty — find out which deploy broke your app