# Kiểm tra kết nối

> Chứng minh từng môi trường chạy của app gửi lỗi tới đúng project: verifyIntegration(), lỗi gắn thẻ cho mọi SDK, mã chẩn đoán và mã sự cố kết nối.

"Không có lỗi" có thể là app khoẻ — hoặc là lỗi của app không bao giờ tới Belvador. **Phép kiểm kết nối** chứng minh, theo từng môi trường chạy, rằng lỗi từ app tới đúng project. Phép kiểm gửi một hoặc hai lỗi có đánh dấu, **không bao giờ thành issue**, và Belvador hiện kết quả ở tab **Kết nối** của project.

## Vì sao issue đầu tiên chưa đủ

Issue đầu tiên chỉ chứng minh một lỗi đã tới một lần. Nó không chứng minh rằng:

- phía máy chủ cũng báo lỗi, không chỉ trình duyệt;
- lỗi không bắt (lỗi không ai bọc trong `try … catch`) có được báo;
- code máy chủ chạy trong middleware của Belvador, nên lỗi được thấy trước khi trang lỗi nuốt mất;
- app vẫn dùng khoá DSN còn hiệu lực sau mỗi lần deploy.

Phép kiểm phủ từng ý trên và lưu kết quả, để cả nhóm thấy lần cuối được chứng minh là khi nào.

## Tab Kết nối hiện gì

Mở project và chọn **Kết nối**. Với mỗi môi trường nền tảng cần — **Trình duyệt**, **Máy chủ**, hoặc cả hai với TanStack Start — bạn thấy:

| Trạng thái | Nghĩa |
|---|---|
| **Đã xác minh** | Một phép kiểm từ môi trường này đã tới và mọi phần nó chứng minh được đều đạt |
| **Chưa kiểm** | Chưa có phép kiểm nào tới trong 30 ngày qua — app có thể đang báo lỗi, cũng có thể không |
| **Đang có vấn đề** (đỏ) | Lỗi đang bị từ chối hoặc bị mất |
| **Đang có vấn đề** (hổ phách) | Lỗi tới được, nhưng một phần app chưa được phủ — ví dụ code máy chủ nằm ngoài middleware |

Sự cố đang mở hiện trên cùng kèm việc cần làm. Sự cố tự đóng khi nguyên nhân hết; quy tắc cảnh báo **Sự cố kết nối SDK** gửi một tin khi sự cố bắt đầu và một tin khi đã hết — xem [Cảnh báo](https://belvador.dev/vi/docs/alerts#canh-bao-su-co-ket-noi).

## Kiểm bằng `verifyIntegration()`

Từ `@belvador/*` 0.2.0, một lời gọi chạy mọi phép kiểm cho môi trường nơi nó được gọi và đọc lại kết quả từ Belvador. Hàm không bao giờ ném lỗi và không tự chạy — hãy chạy một lần sau khi cài và sau những lần deploy đụng tới xử lý lỗi, đừng chạy mỗi lần tải trang.

### Trình duyệt

```ts
import { verifyIntegration } from "@belvador/react";

// Run once after setup or deploy (for example behind a debug flag), never on every page load.
// The console shows one expected "Belvador verification (uncaught)" error — that is the test.
const result = await verifyIntegration();
// result.outcome: "verified" | "problem" | "unconfirmed"; the console prints one summary line.
```

Lời gọi tương tự có trong `@belvador/browser` và `@belvador/tanstack-start/client`. Console trình duyệt sẽ hiện một lỗi có chủ đích, **"Belvador verification (uncaught, expected)"**: phép kiểm cố ý ném lỗi này để chứng minh lỗi không bắt được báo. Truyền `{ uncaught: false }` để bỏ phần đó; khi ấy trình duyệt chỉ được kiểm **đường gửi**.

### Máy chủ (TanStack Start)

```ts title="src/routes/api/verify-belvador.ts"
import { createFileRoute } from "@tanstack/react-router";
import { verifyIntegration } from "@belvador/tanstack-start/server";

// Temporary: open /api/verify-belvador once after deploying, then delete this file.
// It must run inside belvadorRequestMiddleware (src/start.ts) — that is part of the check.
export const Route = createFileRoute("/api/verify-belvador")({
  server: {
    handlers: {
      GET: async () => Response.json(await verifyIntegration()),
    },
  },
});
```

Mở `/api/verify-belvador` một lần sau khi deploy, đọc JSON trả về, rồi xoá tệp. Phép kiểm báo `BLV_HOOK_MISSING` nếu route không chạy trong `belvadorRequestMiddleware`.

### Cloudflare Workers

```ts title="src/index.ts"
import { verifyIntegration } from "@belvador/cloudflare";

// Inside the fetch handler wrapped by withBelvador. Temporary: remove after the check.
if (new URL(request.url).pathname === "/__belvador/verify") {
  return Response.json(await verifyIntegration());
}
```

### Đọc kết quả

`verifyIntegration()` trả về một object và in một dòng, ví dụ:

```text
[belvador] verify server: problem — hook missing (BLV_HOOK_MISSING), delivery confirmed — bv1_3f0c… — https://belvador.dev/en/docs/sdk/verify#blv-hook-missing
```

| Trường | Giá trị |
|---|---|
| `outcome` | `verified` — mọi phép kiểm được Belvador xác nhận; `problem` — có phần không đạt (xem `checks`); `unconfirmed` — đã gửi nhưng Belvador chưa xác nhận được (node offline, tắt đọc lại hoặc hết giờ) |
| `checks[].check` | `init`, `hook`, `delivery`, `uncaught` (trình duyệt) |
| `checks[].status` | `confirmed`, `accepted` (đã gửi, chưa đọc lại), `missing` (đã gửi nhưng không tới), `failed` (kèm `code`), `skipped` |
| `verificationId` | `bv1_` + 32 ký tự hex; cùng mã này hiện ở tab Kết nối |
| `docsUrl` | Mục của trang này cho mã đầu tiên gặp phải |

Tuỳ chọn: `timeoutMs` (mặc định 20000 ms, tối đa 30000), `uncaught` (trình duyệt, mặc định `true`), `readback` (mặc định `true`). Mỗi lời gọi gửi tối đa 2 event và hỏi Belvador tối đa 10 lần.

## Mọi SDK: gửi một lỗi có gắn thẻ

Mọi SDK tương thích Sentry đều chạy được phép kiểm — `@belvador/*` 0.6.1 cũng vậy. Gửi một lỗi có tag `belvador.verification` mang giá trị `bv1_` và 32 ký tự hex chữ thường, rồi đọc lại.

### Gói Belvador trước 0.2.0

```ts
import { captureException, flush, withScope } from "@belvador/react";

// One-time connection check: a marked error that never becomes an issue.
const id = "bv1_" + crypto.randomUUID().replaceAll("-", "");
withScope((scope) => {
  scope.setTag("belvador.verification", id);
  captureException(new Error("Belvador verification"));
});
await flush(5000);
console.log("[belvador] verification id:", id);
```

### Node.js

```js
import * as Sentry from "@sentry/node";

// One-time connection check: a marked error that never becomes an issue.
const id = "bv1_" + crypto.randomUUID().replaceAll("-", "");
Sentry.withScope((scope) => {
  scope.setTag("belvador.verification", id);
  Sentry.captureException(new Error("Belvador verification"));
});
await Sentry.flush(5000);
console.log("[belvador] verification id:", id);
```

### Python

```python
import uuid

import sentry_sdk

# One-time connection check: a marked error that never becomes an issue.
verification_id = "bv1_" + uuid.uuid4().hex
with sentry_sdk.new_scope() as scope:
    scope.set_tag("belvador.verification", verification_id)
    sentry_sdk.capture_exception(Exception("Belvador verification"))
sentry_sdk.flush(5)
print("[belvador] verification id:", verification_id)
```

### Đọc lại

```bash
curl -s "https://ingest.belvador.dev/api/<project-id>/verification/bv1_<id printed by your app>/?sentry_key=<public-key>"
```

Dùng số project và khoá công khai trong DSN. Kết quả liệt kê các phép kiểm đã tới node:

```json
{ "v": 1, "verification_id": "bv1_…", "node": "connected", "checks": [{ "surface": "server", "check": "delivery", "hook": "unknown" }] }
```

Lỗi gắn thẻ chỉ chứng minh đường gửi: SDK Sentry thông thường không cho biết móc lỗi của nó có chạy hay không, nên tab Kết nối hiện **chỉ kiểm đường gửi** cho môi trường đó.

## Mã chẩn đoán của SDK

SDK báo các mã này ngay trong app — ở console trình duyệt khi bật `debug: true`, hoặc qua callback `onDiagnostic` của bạn — tối đa một lần mỗi phút cho mỗi mã. Chúng không bao giờ được gửi về Belvador.

```ts
init({
  dsn: "https://<public-key>@ingest.belvador.dev/<project-id>",
  onDiagnostic: (d) => console.warn(d.code, d.count, d.status),
});
// getDiagnostics() trả về ảnh chụp: đang chạy, tên SDK, host + project + gợi ý khoá DSN, lần gửi cuối, số đếm.
```

### `BLV_DSN_MISSING`

`init()` không nhận được DSN nên SDK tắt. Chép DSN từ trang project.

### `BLV_DSN_INVALID`

DSN không đúng dạng DSN Belvador (`https://<32 hex>@host/<project>`). SDK tắt. Chép lại DSN, không thêm query string.

### `BLV_INIT_CONFLICT`

Trang đã có một client theo dõi lỗi khác nên Belvador không khởi động. Gỡ `Sentry.init` cũ để lỗi không bị báo hai lần.

### `BLV_NOT_INITIALIZED`

`captureException` chạy khi SDK đang tắt — trước `init()`, hoặc sau khi `init()` thất bại. Gọi `init()` càng sớm càng tốt.

### `BLV_SEND_UNAUTHORIZED`

Belvador từ chối khoá DSN (đã thu hồi, hết hạn hoặc của project khác). SDK ngừng gửi tới khi trang hoặc Worker tải lại. Dùng khoá còn hiệu lực trong **Cài đặt project → Khoá DSN**.

### `BLV_SEND_RATE_LIMITED`

Project chạm giới hạn tốc độ hoặc hết hạn mức tháng; event bị bỏ tới khi hết thời gian chờ (`retryAfterS`). Xem [giới hạn nhận lỗi](https://belvador.dev/vi/docs/projects-and-dsn#gioi-han-nhan).

### `BLV_SEND_REJECTED`

Một event không được nhận — thường do quá lớn. Giữ dữ liệu phụ và tệp đính kèm nhỏ.

### `BLV_SEND_NETWORK`

Không tới được Belvador sau khi thử lại. Kiểm tra mạng, trình chặn quảng cáo và `connect-src` của Content-Security-Policy có cho phép `https://ingest.belvador.dev`.

### `BLV_SEND_SERVER`

Belvador trả lỗi máy chủ sau khi thử lại. Thường chỉ tạm thời; nếu kéo dài, xem trạng thái node.

### `BLV_QUEUE_FULL`

Quá nhiều event chờ gửi cùng lúc nên một event bị bỏ. Giảm lỗi lặp dồn dập bằng `ignoreErrors` hoặc `sampleRate`.

### `BLV_EVENT_TOO_LARGE`

Một event hoặc tệp đính kèm vượt giới hạn kích thước và bị bỏ trước khi gửi — xem [Cấu hình SDK](https://belvador.dev/vi/docs/sdk/configuration).

### `BLV_HOOK_MISSING`

Phép kiểm chạy trong code máy chủ không nằm trong middleware hay Worker wrapper của Belvador, nên lỗi ném ở đó không được báo tự động. Đăng ký `belvadorRequestMiddleware` và `belvadorFunctionMiddleware` ở cuối danh sách trong `src/start.ts` — xem [TanStack Start](https://belvador.dev/vi/docs/sdk/tanstack-start) — hoặc bọc Worker bằng `withBelvador`.

### `BLV_DROPPED_BY_APP`

Phép kiểm chưa rời khỏi app: `beforeSend` của bạn trả `null`, `ignoreErrors` khớp với nó, hoặc `sampleRate` dưới 1 đã bỏ nó. Hãy cho lỗi tên `BelvadorVerificationError` đi qua.

### `BLV_MONITOR_INVALID_SLUG`

Slug của monitor chỉ gồm chữ thường, số, `-` và `_`, tối đa 64 ký tự (ví dụ `nightly-invoices`). Công việc vẫn chạy; không có check-in nào được gửi cho nó — xem [Monitor](https://belvador.dev/vi/docs/monitors/setup).

### `BLV_MONITOR_INVALID_REASON`

Lý do truyền vào `run.skip()`, `run.unknown()` hoặc `captureCheckIn` phải là mã ngắn như `provider_timeout`, không phải câu thông báo. Lý do đã bị bỏ khỏi check-in.

### `BLV_MONITOR_CONFIG_INVALID`

Lịch hoặc một cài đặt truyền vào `withMonitor` không hợp lệ (crontab không đúng 5 trường, múi giờ lạ, số ngoài khoảng cho phép). Check-in vẫn được gửi nhưng không kèm cấu hình, nên cài đặt của monitor không đổi.

### `BLV_CHECK_IN_DROPPED`

Một check-in không được gửi: quá nhiều check-in chờ cùng lúc (tối đa 10 trong 30 lượt gửi đang chờ), check-in bị giới hạn tốc độ, hoặc mạng lỗi. Lỗi (error) không bị ảnh hưởng. Nếu lặp lại, monitor có thể hiện "trễ" — xem lại tần suất công việc báo về.

### `BLV_TRACE_TARGET_INVALID`

Một mục trong `tracePropagation.targets` không phải chuỗi hay biểu thức chính quy nên bị bỏ qua; request tới đó không được gắn header trace.

### `BLV_OTEL_INVALID`

Tuỳ chọn `otel.api` không phải module `@opentelemetry/api` (truyền `import * as api from "@opentelemetry/api"`, đúng bản mà OpenTelemetry SDK của app đang dùng). Cầu nối tắt; lỗi vẫn được ghi nhận nhưng không kèm id của span.

### `BLV_TRACE_OWNER_OTEL`

Đặt cả `otel` lẫn `tracePropagation`. OpenTelemetry đã tự truyền trace nên Belvador không gắn header trace riêng và bỏ qua `tracePropagation`. Xoá `tracePropagation` để hết thông báo này.

### `BLV_PRIVACY_POLICY_INVALID`

Từ SDK 0.6.0. `privacyPolicy()` nhận JSON không phải chính sách hợp lệ từ trang Quyền riêng tư của project (sai schema, pattern không an toàn, selector sai). App vẫn gửi nhưng không áp luật của project; node vẫn áp chính sách hiện tại của project trước khi lưu. Sao chép lại bằng "Sao chép dạng code".

### `BLV_PREVIEW_ONLY`

Từ SDK 0.6.0. Đang bật chế độ xem trước (`preview: true` hoặc `BELVADOR_PREVIEW=1`): mỗi envelope được in ra sau khi đã áp mọi luật và bộ che, và không gửi gì. Lần kiểm tra kết nối trong chế độ này không tới Belvador. Tắt xem trước để gửi.

### `BLV_OFFLINE_UNAVAILABLE`

Từ SDK 0.6.0. Đã đặt `offline: true` nhưng trình duyệt này không dùng được IndexedDB (một số cửa sổ ẩn danh, chính sách chặn lưu trữ) hoặc không tải được module offline (CSP chặn chunk của chính app). Lỗi vẫn được gửi nhưng không có hàng đợi offline: lỗi nào hỏng mạng khi gửi thì mất. Xem [Offline và gửi bền](https://belvador.dev/vi/docs/sdk/offline).

## Mã sự cố kết nối

Belvador mở các sự cố này từ những gì edge thấy và từ phép kiểm. Mỗi sự cố ghi rõ project và, khi áp dụng, môi trường chạy.

### `INTEGRATION_KEY_INACTIVE`

App vẫn gửi bằng khoá DSN đã thu hồi hoặc hết hạn; các lỗi đó bị từ chối. Đặt DSN còn hiệu lực vào app rồi deploy lại.

### `INTEGRATION_ORIGIN_BLOCKED`

Lỗi tới từ website không có trong **Tên miền cho phép** của project. Thêm domain, hoặc để trống danh sách để nhận mọi domain.

### `INTEGRATION_RATE_LIMITED`

Nhiều lỗi bị bỏ vì giới hạn tốc độ của project. Nâng giới hạn trong Cài đặt project hoặc giảm lỗi lặp.

### `INTEGRATION_QUOTA_EXCEEDED`

Workspace đã dùng hết hạn mức tháng; lỗi bị bỏ tới đầu tháng sau.

### `INTEGRATION_TOO_LARGE`

Nhiều payload quá lớn. Tắt tệp đính kèm lớn và giữ dữ liệu phụ nhỏ.

### `INTEGRATION_INVALID_EVENTS`

Node không đọc được một số payload — thường do SDK không được hỗ trợ hoặc transport tự viết.

### `INTEGRATION_BUFFER_LOSS`

Node offline đủ lâu để lỗi đang chờ hết hạn hoặc phòng chờ đầy. Đưa node trở lại từ trang Node.

### `INTEGRATION_SDK_DROPS`

SDK trong app báo không gửi được lỗi (mạng, CSP hoặc hàng đợi đầy). Bật `debug` để thấy lý do trong console.

### `INTEGRATION_HOOK_MISSING`

Một phép kiểm máy chủ chạy ngoài middleware của Belvador — xem `BLV_HOOK_MISSING` ở trên.

### `INTEGRATION_UNCAUGHT_MISSING`

Một phép kiểm trình duyệt báo trước một lỗi không bắt nhưng lỗi đó không tới trong 2 phút: có thứ thay thế `window.onerror` hoặc `init()` chạy quá muộn.

## Giới hạn

- Một phép kiểm chứng minh đúng đường nó đã chạy, tại thời điểm đó. Những chỗ code tự bắt lỗi vẫn cần `captureException`.
- Lỗi không bắt ở trình duyệt được cố ý ném trong `setTimeout`; bạn có thể thấy nó trong console và trong tiện ích trình duyệt theo dõi lỗi.
- Phép kiểm bị giới hạn theo project (60 lần mỗi giờ) và theo khoá (30 lần đọc lại mỗi phút). Phép kiểm không bao giờ tính là issue và chỉ được giữ dạng metadata trong 30 ngày.
- Node cần phiên bản 0.1.17 trở lên để nhận biết phép kiểm; node cũ hơn hiện banner trên tab Kết nối.
