クライアントエージェント
Browser
サーバーからは見えないもの。実際の人にとってページが本当はどれだけかかったのか、どのクリックが何もしなかったのか、そしてどのコンポーネントが400回再描画されたのか。
- パッケージ
@sixty-sh/browser/ npm- 動作環境
- どんなページでも。観測するリクエスト1件あたり、ブロッキングのコストはおよそ2.4マイクロ秒。
- ソース
- sixty-sh/sixty-browser
導入方法
Next, Remix, SvelteKit and friends. The key stays server-side behind a proxy route.
導入手順は、あなた向けのチェックリストではなく、すでに開いているコーディングエージェント向けのプロンプトとして書かれています。これは意図的です。どのファイルを編集するかではなく、導入が終わった時点で何が成り立っていなければならないかを述べています。コードをどこに置くかはフレームワーク次第で、置き場所を間違えると静かに失敗するからです。エージェントはリポジトリを読んでそれを判断できますが、ドキュメントの段落にはできません。
同じ文面は、 install_sixty が MCP サーバー 経由で返すものであり、コレクタが次の場所で配信しているものでもあります: /v1/setup?kind=browser. 実体は1つだけです。
Browser の導入手順(全文)
Set up @sixty-sh/browser in this project so page performance reports to sixty.
Before editing, ask the user: "Do you want privacy-masked session replay?"
Do not choose for them. If yes, use the replay-enabled init in step 3. If no,
use plain init() and do not add replay configuration.
1. Install @sixty-sh/browser.
2. Add a server-side endpoint at the path /api/drift whose GET and POST
handlers are both the same createSixtyProxy() handler 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. POST receives measurements and recordings;
GET reads the Sessions policy. Omitting either method is an incomplete install.
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). If the user chose replay, call
init({ replay: { enabled: true } }); otherwise call init(). The default
endpoint is /api/drift.
@sixty-sh/browser owns the compatible recorder dependency, so do not install
or import rrweb separately. Replay remains privacy-masked and the Sessions
setting decides whether recordings are off, incident-only, or sampled.
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.秘密鍵が必要です — sixty_sk_ で始まり、サーバー側に留まります。ログイン後、設定ページで発行してください。
何を測るか
| シグナル | 単位 | 意味 |
|---|---|---|
web_vital | ms | a page-speed metric got worse for real users |
render_storm | renders | a component re-renders many times for one interaction |
dead_interaction | of clicks | users click this and nothing observable happens |
stuck_loading | of loads never finish | a loading state is entered and never left |
client_error | of views | this error is being thrown in real users’ browsers |
auth_failures | — | The server is turning these away on permission grounds rather than failing. People see an empty page or a save that quietly does nothing. |
silent_empty | — | The 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. |
latency | ms per call | this operation takes longer end to end than it used to |
errors | error rate | a larger fraction of calls are throwing |
runaway | calls per minute | this operation is being called far more often than anything triggers it |
どこに入り込むか
- Next、Remix、SvelteKit — 自前のサーバーがあるということは、キーはプロキシ用のルートの後ろでサーバー側に留まるということです — Lovable 用ではなく、こちらの段を選んでください。
- サーバーがまったくない — Lovable、静的ホスティング、Supabase だけのアプリ:オリジンに固定された公開鍵を使う Lovable の段を見てください。
これだけができること
- ルート別・国別のページ速度 — 実際の訪問から得た LCP、INP、CLS、TTFB を、ラボの測定ではなく前のリリースと比較します。
- レンダーのループ — 1回の操作がページを数百回描き直すこと。サーバー側からは何も見えず、エラーも投げられません。
- 何もしないクリック — 押されても測定できる変化を何も生まない操作部品 — リクエストも、遷移も、DOM の変化もなし。
- 終わらない読み込み — 入ったまま二度と出てこない読み込み状態。問い合わせは発生するのにログの行は1つも出ない、あの不具合です。
できないこと
- どんな種類の利用者の識別情報も収集しません — ID もセッションもクッキーもなし。これは設定ではなく設計上の制約なので、「どの利用者がこれを見たのか」は、私たち自身も含め誰に対しても答えられない問いです。
- 本番ではスタックフレームはバンドルされたチャンクを指します。ブラウザのエラーを識別するのは操作名とメッセージであって、行番号ではありません。
設定
どのエージェントも同じ4つの変数を読みます。そして SIXTY_* が答えるところでは DRIFT_* も引き続き答えます — 製品名は変わりましたが、その名前は他人のデプロイから引き上げてよい類のものではありません。
SIXTY_API_KEY | これがないとエージェントは何もせず、そのことを伝えます。推測もせず、未知のエンドポイントに再試行もせず、例外も投げません。 |
|---|---|
SIXTY_SERVICE | このサービスを何と呼ぶか。読み取れる場合はプロジェクト名が既定になります。 |
SIXTY_RELEASE | いちばん重要なもの。Vercel、Render、Railway、Fly、Heroku、GitHub Actions では自動で拾われます。それ以外ではコミットの SHA を設定してください。これがないとすべての計測が名前のない1つのバケツに入り、比較は永遠にできません。 |
SIXTY_ENDPOINT | どこへ報告するか。既定は http://localhost:4319で、ノートPCの上では正しく、そのアプリが他人に配信された瞬間に間違いになります。 |
残り — 送信間隔、サンプリング率、何を計測するか — はパッケージ自身の README にあります。エージェントが変わっても正しいままでいられる場所だからです。