# Phiên trình duyệt

> Phiên là một lần chạy hồ sơ trên một máy — vòng đời, vé gắn kết và cách kết thúc.

Một **phiên** là một hồ sơ đang mở trên một máy. Mỗi hồ sơ có tối đa một phiên đang chạy, và mọi phiên đều có hàng rào thế hệ: máy đã mất quyền giữ phiên không thể hành động thay phiên đó nữa.

App chạy nhiều tài khoản có thể giữ trình duyệt của hồ sơ thay vì tự mở và tắt — xem [App giữ trình duyệt](https://browlix.com/docs/app-sessions).

## Vòng đời

| Trạng thái | Ý nghĩa | Nên làm gì |
|---|---|---|
| `queued` | Đã nhận, chờ máy | Chờ |
| `preflight` | Kiểm tra mạng, proxy, dữ liệu hồ sơ | Chờ |
| `starting` | Đang mở trình duyệt | Chờ |
| `ready` | Trình duyệt đang chạy; gắn kết được | Gắn kết hoặc xem |
| `quiescing` | Hoàn tất trước khi dừng (đang lưu dữ liệu) | Chờ |
| `stopping` | Đang đóng trình duyệt | Chờ |
| `closed` | Đã kết thúc; không còn tiến trình trình duyệt | Xong |
| `failed` | Không mở được hoặc kết thúc vì lỗi | Đọc lý do rồi mở lại |
| `recovery_required` | Kết thúc không chắc chắn; hồ sơ cần quyết định | [Phục hồi](#phuc-hoi), rồi mở lại |

Hỏi lại `sessions.get`, hoặc đăng ký [webhook](https://browlix.com/docs/webhooks) `session.ready`, `session.failed`, `session.closed`. `waitForSession` của SDK và `sessions wait` của CLI chỉ chờ: dừng chờ không bao giờ dừng phiên.

## Mở phiên

`sessions.start` cần `profile_id`, `device_id` và `command_id`. Các lý do từ chối:

- `BUSY` — hồ sơ đang mở; `error.details` cho biết phiên đó.
- `SYNC_PENDING` — máy khác còn giữ dữ liệu mới hơn của hồ sơ.
- `RECOVERY_REQUIRED` — phiên trước kết thúc không chắc chắn; `error.details` chỉ ra phiên đó. Xem [Phục hồi](#phuc-hoi).
- `QUOTA_EXCEEDED` — workspace đã chạy đủ số phiên đồng thời theo giới hạn.

## Gắn kết

`sessions.attach` cấp vé cho một phiên đang `ready`, dùng trong `ttl_seconds` (30–900, mặc định 300), kèm địa chỉ broker cục bộ của máy. Kết nối từ chính máy đó với vé làm bearer header — xem [Gắn Playwright](https://browlix.com/docs/playwright). Đóng kết nối Playwright không dừng phiên.

## Dừng phiên

`sessions.stop` là lệnh duy nhất kết thúc phiên. Phiên chỉ báo `closed` khi máy chứng minh không còn tiến trình trình duyệt nào; sau đó dữ liệu hồ sơ được niêm phong và tải lên.

## Phục hồi

Một phiên kết thúc *không chắc chắn* khi máy của nó thôi xác nhận — tắt máy, mất mạng, bị xoá sạch — hoặc báo đã dừng mà không chứng minh được. Browlix không đoán: phiên chuyển sang `recovery_required` và hồ sơ đóng ở mọi nơi, nên không bao giờ có hai trình duyệt cùng ghi một hồ sơ.

Khi chắc chắn máy đã mất, gọi `sessions.recover` kèm `command_id`. Phiên được chốt là `failed` với lý do `RECOVERED`, hồ sơ mở lại được (fence cao hơn) và báo cáo đến muộn của máy cũ bị từ chối; nếu trình duyệt cũ quay lại, nó được yêu cầu dừng. Lệnh cần `device.manage` — owner hoặc admin, và key có scope đó — và được ghi audit. Khi máy vẫn còn xác nhận phiên, lệnh trả `CONFLICT`: hãy dừng phiên thay vì phục hồi.

```bash
curl -s -X POST "$BROWLIX_API/sessions/$SESSION_ID/recover" \
  -H "Authorization: Bearer $BROWLIX_API_KEY" -H "Content-Type: application/json" \
  -d "{\"command_id\":\"$(uuidgen)\"}"
```

Nếu máy đó còn giữ dữ liệu hồ sơ chưa tải lên, mở ở nơi khác vẫn trả `SYNC_PENDING`; `profiles.sync.release` bỏ bản sao trên máy đó một cách có chủ đích.

## Lịch sử

`sessions.list` với `scope=live` trả về phiên đang mở; `scope=recent` trả về 7 ngày gần nhất, có thể lọc một hồ sơ bằng `profile=<id hồ sơ>`.
