sixty

client agent

Browser

What the server cannot see: how long the page actually took for a real person, which clicks did nothing, and which component re-rendered four hundred times.

package
@sixty-sh/browser on npm
runs on
Any page. Roughly 2.4µs of blocking cost per request it observes.
source
packages/browser

Installing it

Next, Remix, SvelteKit and friends. The key stays server-side behind a proxy route.

The install is written as a prompt for the coding agent you already have open, not as a checklist for you. That is deliberate: it names what must be true when the install is finished rather than which files to edit, because where the code goes depends on the framework and putting it in the wrong place fails silently. An agent can read your repository and work that out; a paragraph on a documentation page cannot.

The same text is what install_sixty returns through the MCP server and what the collector serves at /v1/setup?kind=browser. There is one copy of it.

the Browser install, in full
Set up @sixty-sh/browser in this project so page performance reports to sixty.

1. Install @sixty-sh/browser.

2. Add a server-side POST endpoint at the path /api/drift that is handled by
   createSixtyProxy() from "@sixty-sh/browser/proxy". Work out the correct
   file and export style for this project's framework and router. It must run
   on the server, not the client.

3. Call init() from "@sixty-sh/browser" exactly once in the browser, in a
   client component that is mounted on every page (the root layout is usually
   right). It takes no arguments — the default endpoint is /api/drift.

4. Set these server-side environment variables (never client-exposed ones):
      SIXTY_API_KEY   = a secret key starting sixty_sk_ — ask me for it, I can
                        generate one on the Settings page. Do not invent one.
      SIXTY_SERVICE   = my-app
      SIXTY_ENDPOINT  = https://ingest.sixty.sh

5. If this project has a router that knows its route patterns, call setRoute()
   from "@sixty-sh/browser" with the pattern (e.g. "/orders/[id]") on each
   navigation. Without it, routes are guessed from the URL, which is close but
   not exact.

Constraints — these are correctness requirements, not style preferences:
- SIXTY_API_KEY must never reach the browser. Do not prefix it with NEXT_PUBLIC_,
  VITE_, or PUBLIC_, do not pass it to init(), and do not import it into any
  client component. The whole point of the proxy is that the key stays server-side.
- Do not add any analytics, user id, session id, or cookie. The agent is
  deliberately anonymous and must stay that way.
- Do not call init() during server rendering. It returns null there, so guard it
  in an effect or a client-only component rather than at module scope.

When you are done, tell me which files you changed and how to deploy so I can
confirm data is arriving.

It needs a secret key — it starts sixty_sk_ and stays server-side. Mint one on the Settings page once you have signed in.

What it measures

signalunitwhat it means
web_vitalmsa page-speed metric got worse for real users
render_stormrendersa component re-renders many times for one interaction
dead_interactionof clicksusers click this and nothing observable happens
stuck_loadingof loads never finisha loading state is entered and never left
client_errorof viewsthis error is being thrown in real users’ browsers
auth_failuresThe server is turning these away on permission grounds rather than failing. People see an empty page or a save that quietly does nothing.
silent_emptyThe query runs and succeeds, and returns no rows where it used to return plenty. Nothing reports an error, so the page just renders blank — this is what a broken permission rule looks like from the outside.
latencyms per callthis operation takes longer end to end than it used to
errorserror ratea larger fraction of calls are throwing
runawaycalls per minutethis operation is being called far more often than anything triggers it

Where it hooks in

  • Next, Remix, SvelteKitA server of your own means the key stays server-side behind a proxy route — take this tier, not the Lovable one.
  • No server at allLovable, a static host, a Supabase-only app: see the Lovable tier, which uses a public, origin-pinned key.

What only this one does

  • Page speed per route and per countryLCP, INP, CLS and TTFB from real visits, compared against the last release rather than against a lab run.
  • Render loopsOne interaction that re-renders the page hundreds of times. Nothing server-side can see this and no error is thrown.
  • Clicks that do nothingA control that is clicked and produces no measurable change — no request, no navigation, no DOM mutation.
  • Never-finished loadingA loading state entered and never left, which is the failure that produces a support ticket and no log line.

What it cannot do

  • No user identity of any kind is collected — no id, no session, no cookie. That is a design constraint rather than a setting, so "which user saw this" is a question this product cannot answer for anybody, including us.
  • Stack frames in production point at bundled chunks. The operation name and the message are what identify a browser error, not the line number.

Configuration

Every agent reads the same four variables, and DRIFT_* still answers everywhere SIXTY_* does — the product was renamed and that name is not ours to retire from other people’s deployments.

SIXTY_API_KEYWithout it the agent stays inert and says so. It never guesses, never retries against an unknown endpoint, and never throws.
SIXTY_SERVICEWhat to call this service. Defaults to the project name where one is legible.
SIXTY_RELEASEThe one that matters most. Picked up automatically on Vercel, Render, Railway, Fly, Heroku and GitHub Actions; set it to the commit SHA anywhere else. Without it every measurement lands in a single nameless bucket and no comparison can ever be made.
SIXTY_ENDPOINTWhere to report. Defaults to http://localhost:4319, which is right on a laptop and wrong the moment the app is served to anyone else.

The rest — flush interval, sample rate, what to instrument — is in the package’s own README, which is where it can stay true as the agent changes.

The sixty Browser agent — what it measures and how to install it