remote.Tài liệuTrang chủ

Giao thức truyền thông

Tài liệu này chỉ định định dạng dây hoàn chỉnh giữa agent, exchange và cli. Definitions live in crates/proto, and all three share the same code: fields are defined in one place, so inconsistencies surface at compile time rather than during a production session.

Nó phục vụ các nhà khai thác kiểm tra lưu lượng mạng để chẩn đoán sự cố và các nhà phát triển thay đổi giao thức hoặc viết ứng dụng khách của bên thứ ba.


Nội dung


Tổng quan

Giao thức có hai lớp chia sẻ một kết nối WebSocket:

Lớp Hãng vận chuyển Mã hóa Nội dung
Kiểm soát tin nhắn Khung WebSocket text khung JSON Đăng ký, nhịp tim, phiên khai mạc/kết thúc, thông báo kết quả
Khung dữ liệu Khung WebSocket binary khung Tiêu đề cố định 9 byte + byte thô Luồng byte PTY, lệnh stdout/stderr

Việc nhúng dữ liệu phát trực tuyến vào JSON dưới dạng base64 sẽ tăng kích thước của nó lên khoảng 33%. Lưu lượng PTY bao gồm các gói nhỏ thường xuyên lặp lại mỗi lần nhấn phím, do đó chi phí này ảnh hưởng trực tiếp đến độ trễ tương tác. Thông báo điều khiển không thường xuyên, làm cho khả năng đọc JSON trở nên có giá trị hơn: việc chụp gói có thể hiểu được ngay lập tức.

Theo quy ước, mỗi khung nhị phân WebSocket ** mang chính xác một khung dữ liệu**. Extra or missing bytes are errors.


Giao thông vận tải và điểm cuối

Điểm cuối khách hàng Xác thực
GET /ws/device agent The first frame after connection establishment must be a register control message containing device_id and token
GET /ws/operator remote-cli Tiêu đề yêu cầu HTTP Authorization: Bearer <运维令牌> trong bắt tay WebSocket

The two paths use separate credential types. Mã thông báo thiết bị không thể truy cập điểm cuối của nhà điều hành và ngược lại.

Operator credentials use a request header rather than a URL query string because URLs appear in reverse proxy logs, browser history, and Referer headers. Device tokens use the first frame rather than a request header because some embedded clients cannot easily customize WebSocket handshake headers. Cách tiếp cận khung đầu tiên cũng cho phép phiên bản giao thức không khớp và mã thông báo không hợp lệ sử dụng cùng một thông báo phản hồi.

Việc triển khai sản xuất phải sử dụng wss://: cả hai loại thông tin xác thực đều xuất hiện ở dạng văn bản gốc trong quá trình bắt tay.


Phiên bản giao thức

pub const PROTOCOL_VERSION: u16 = 1;

Các phiên bản được so sánh theo cả hai hướng trong quá trình bắt tay. Sự không phù hợp sẽ gây ra sự từ chối ngay lập tức kèm theo lời giải thích rõ ràng. Điều này ngăn không cho các phiên bản không khớp xuất hiện dưới dạng lỗi phân tích cú pháp không thể giải thích được trong thông báo sau, điều này sẽ khó chẩn đoán.

  • Phía thiết bị: agent bao gồm phiên bản của nó trong register. Khi không khớp, máy chủ trả về register_ack có ok: false, mô tả cả hai phiên bản trong error và đóng kết nối.
  • Phía nhà điều hành: máy chủ chủ động gửi welcome sau khi thiết lập kết nối. Nếu CLI phát hiện sự không khớp, nó sẽ báo lỗi và hướng dẫn người dùng nâng cấp lên phiên bản phù hợp với server_version.

Thông báo không xác định Giá trị type luôn bị từ chối, không bao giờ được hiểu âm thầm là loại đã biết.


Kiểm soát tin nhắn

Tất cả các thông báo điều khiển là các đối tượng JSON được gắn thẻ bởi trường type (các enum được gắn thẻ nội bộ serde). Giá trị thẻ và tên trường sử dụng snake_case. session_id được tuần tự hóa một cách rõ ràng dưới dạng số đơn giản.

DeviceUp (agent → exchange)

type Trường Mục đích
register protocol_version: u16
device_id: string
token: string
info: DeviceInfo
Phải là thông báo first sau khi kết nối. Máy chủ không chấp nhận tin nhắn nào khác trước nó, với thời hạn 15 giây
heartbeat metrics: Metrics Các chỉ số hoạt động định kỳ Connection liveness is handled separately by native WebSocket ping/pong: a missing heartbeat does not imply a broken connection, or vice versa
exec_started session_id
job_id
hold
timeout_secs
Lệnh bắt đầu; job_id được sử dụng trong danh sách giữ và để gắn lại sau khi kết nối lại
exec_exit session_id
code: i32 | null
timed_out: bool
Lệnh đã xong. code là null khi có tín hiệu kết thúc quá trình; timed_out biểu thị việc buộc phải chấm dứt do hết thời gian chờ
exec_list_resp session_id
jobs: ExecHoldInfo[]
Danh sách việc làm đang giữ
ubus_resp session_id
code: i32
data: any
kết quả cuộc gọi ubus. code is the ubus return code; 0 nghĩa là thành công
android_resp session_id
result: AndroidResult
Android screen operation completed; this message ends the session
pty_closed session_id
code: i32 | null
Phiên PTY kết thúc; code is the shell exit code
session_error session_id
message: string
Lỗi cấp phiên. Thiết bị sẽ gửi thông báo này khi không thể hoàn thành yêu cầu thay vì âm thầm hủy yêu cầu

register thứ hai trên kết nối đã đăng ký được coi là sử dụng sai giao thức và được ghi lại dưới dạng cảnh báo.

Ví dụ:

{"type":"register","protocol_version":1,"device_id":"lab-router","token":"a1b2….c2Vj…","info":{"hostname":"OpenWrt","model":"ath79/generic","openwrt_version":"OpenWrt 23.05.5","kernel":"5.15.137","arch":"mips","agent_version":"0.1.0"}}
{"type":"exec_exit","session_id":31,"code":0,"timed_out":false}

DeviceDown (exchange → agent)

type Trường Mục đích
register_ack ok: bool
error: string | null
heartbeat_interval_secs: u64
Kết quả đăng ký. Với ok: false, error đưa ra lý do, sau đó máy chủ sẽ đóng kết nối. Máy chủ chỉ định khoảng nhịp tim để có thể giảm tần số tập trung khi nhóm phát triển mà không cần cấu hình lại từng thiết bị. agent kẹp 0 đến ít nhất 1 giây để ngăn bộ hẹn giờ bận làm bão hòa CPU
device_access managers: DeviceManager[]
updated_at: u64 (mili giây)
Who has permission on this device. DeviceManager = operator_id, username, email?, role, level (manage/view), source (role_default/grant). Chỉ được gửi sau khi đăng ký và thay đổi quyền cho các đại lý quảng cáo khả năng device_access; see repository docs/permissions.md
exec_req session_id
command: string
timeout_secs: u64
hold: bool (mặc định true)
hold_timeout_secs?: u64
Thực hiện một lệnh. Với hold=true (mặc định), quá trình có thể tiếp tục sau khi người vận hành ngắt kết nối. Thời gian chờ sử dụng hold_timeout_secs (vắng mặt/0 → 3600, cứng tối đa 86400). hold=false hủy lệnh khi người vận hành ngắt kết nối
exec_cancel session_id Chấm dứt (kill) việc thực hiện lệnh sớm
exec_detach session_id Dừng truyền phát đầu ra đến toán tử hiện tại trong khi vẫn giữ quá trình giữ
exec_list_req session_id Truy vấn công việc giữ hiện tại
exec_attach_req session_id
job_id
Gắn lại công việc lưu giữ vào phiên này và phát lại đầu ra được lưu vào bộ đệm
exec_hold_kill session_id
job_id
Terminate a hold job by job_id
exec_hold_tail session_id
job_id
Chỉ theo dõi đầu ra mới mà không phát lại bộ đệm
exec_hold_cat session_id
job_id
Kết xuất bộ đệm hiện tại và kết thúc bằng exec_hold_cat_done
ubus_req session_id
object: string
method: string
params: any
Thực hiện cuộc gọi ubus. Khi params là null, không có đối số vị trí thứ ba nào được chuyển tới ubus
pty_open session_id
rows: u16
cols: u16
Mở phiên PTY. Kích thước 0 giảm trở lại 24 × 80
pty_resize session_id
rows: u16
cols: u16
Terminal window dimensions changed
pty_close session_id Đóng phiên PTY
android_req session_id
request: AndroidAction
One Android screen operation
android_cancel session_id Hủy yêu cầu Android chưa hoàn thành

Toán tửUp (cli → exchange)

Quá trình xác thực diễn ra trong quá trình bắt tay WebSocket, do đó không có thông báo đăng nhập.

type Trường Mục đích
open_exec session_id
device_id
command
timeout_secs
hold (mặc định đúng)
hold_timeout_secs?
Execute a command on the target device. giữ được bật theo mặc định; hold=true có thời gian chờ 0 → 3600 giây. hold=false có thời gian chờ 0 → Exchange mặc định là 60 giây
list_exec_holds session_id
device_id
Truy vấn công việc thực thi và tải xuống, tải lên và truy vấn thư mục đang hoạt động
list_device_sessions session_id
device_id
Truy vấn trực tiếp bảng định tuyến Exchange mà không tốn khe phiên thiết bị
kill_device_session session_id
device_id
target_session_id
Hủy theo target_session_id phía thiết bị và giải phóng tuyến cục bộ; bao gồm lệnh thực thi giữ và không tiêu tốn thêm khe cắm
attach_exec session_id
device_id
job_id
Đính kèm công việc giữ và tiếp tục nhận đầu ra
exec_hold_kill / exec_hold_tail / exec_hold_cat session_id
device_id
job_id
giết/đuôi/mèo để giữ việc
detach_session session_id Dừng chờ đợi ở phía trước và tách ra mà không giết quá trình giữ
open_ubus session_id
device_id: string
object: string
method: string
params: any
Thực hiện cuộc gọi ubus
open_pty session_id
device_id: string
rows: u16
cols: u16
Mở phiên PTY tương tác
open_android session_id
device_id: string
request: AndroidAction
Initiate an Android screen operation; read-only accounts are prohibited
pty_resize session_id
rows: u16
cols: u16
Terminal window dimensions changed. Không có device_id: phiên đã tồn tại nên máy chủ có thể tra cứu thiết bị đích
close_session session_id Chủ động kết thúc phiên: thoát PTY bình thường hoặc người dùng gián đoạn thực thi lệnh

The session_id in all four open_* messages is allocated by the CLI itself (see Session ID Translation Between Two Namespaces), allowing subsequent frames immediately after the request without waiting for an ID acknowledgment.

OperatorDown (exchange → cli)

type Trường Mục đích
welcome protocol_version: u16
server_version: string
Được máy chủ chủ động gửi sau khi thiết lập kết nối để xác minh các phiên bản giao thức phù hợp
device_sessions session_id
sessions: DeviceSessionInfo[]
limit
count
Current device-side sessions in Exchange
device_session_killed session_id
target_session_id
released: bool
Phát hành tại địa phương; false if it did not exist
exec_exit session_id
code: i32 | null
timed_out: bool
Lệnh đã kết thúc, được dịch từ thông báo cùng tên phía thiết bị
ubus_resp session_id
code: i32
data: any
kết quả cuộc gọi ubus
android_resp session_id
result: AndroidResult
Kết quả Android với ID phiên được dịch sang không gian tên toán tử
pty_closed session_id
code: i32 | null
Phiên PTY đã kết thúc
session_failed session_id
reason: SessionFailure
message: string
Phiên thất bại. reason phân biệt các nguyên nhân để CLI có thể quyết định xem việc thử lại có đáng hay không

Cấu trúc dữ liệu được chia sẻ

ExecHoldInfo: job_id, kind (exec / download / upload / file), age_secs, session_id?, command_preview, status, timeout_secs, started_at_unix_ms, exit_code?, timed_out. For compatibility, missing kind defaults to exec, and missing age_secs defaults to 0. List responses calculate age from the current clock. Tóm tắt đường dẫn lệnh/tệp được giới hạn ở khoảng 120 ký tự. Các công việc không thực thi chỉ hỗ trợ tiêu diệt, không hỗ trợ tail/cat/attach.

DeviceSessionInfo: session_id: u32 (ID phía thiết bị), operator_session_id: u32, kind: string, hold: bool, age_secs: u64, deadline_unix_ms: u64. Exchange trả lời trực tiếp các yêu cầu danh sách phiên và buộc hủy mà không cần mở phiên thiết bị khác; read-only accounts receive forbidden.

DeviceInfo

Thông tin thiết bị tĩnh, được báo cáo một lần trong register và được lưu trữ trong devices.info. Mọi thứ ngoại trừ agent_version đều là tùy chọn: hầu hết các nguồn không khả dụng khi agent chạy bên ngoài OpenWrt, chẳng hạn như trên máy phát triển hoặc trong vùng chứa. Đăng ký vẫn thành công chứ không phải khởi động thất bại. Missing fields are omitted during serialization.

trường Loại Nguồn
hostname string? /proc/sys/kernel/hostname
model string? DISTRIB_TARGET trong /etc/openwrt_release, quay trở lại model từ ubus call system board nếu không có
openwrt_version string? DISTRIB_DESCRIPTION, or ubus release.description
kernel string? /proc/sys/kernel/osrelease
arch string? Hằng số thời gian biên dịch, đáng tin cậy hơn phát hiện thời gian chạy
agent_version string Phiên bản agent, dùng để chẩn đoán phiên bản không khớp

Metrics

Được thu thập lại cho mỗi nhịp tim, chỉ sử dụng các giá trị có sẵn trực tiếp từ /proc mà không phân tách các quy trình con. Việc không đọc được một mục không làm hỏng toàn bộ bộ sưu tập: thà mất một số liệu còn hơn là không đọc được toàn bộ nhịp tim và báo cáo thiết bị ngoại tuyến không chính xác.

trường Loại Mô tả
uptime_secs u64 /proc/uptime
load [f32; 3] 1/5/15-minute load averages from /proc/loadavg
mem_total_kb u64 /proc/meminfo
mem_available_kb u64 Sử dụng MemAvailable thay vì MemFree: cái sau loại trừ bộ đệm có thể lấy lại được và đánh giá thấp đáng kể bộ nhớ khả dụng trên bộ định tuyến, tạo ra cảnh báo sai
cpu_usage_pct f32? Tính toán từ sự chênh lệch giữa hai mẫu /proc/stat; bị bỏ qua ở nhịp tim đầu tiên vì không có mẫu tham chiếu
interfaces InterfaceStats[] /proc/net/dev. Báo cáo cumulative nhận/truyền byte thay vì tốc độ, giúp máy chủ tự do tính toán tốc độ trong bất kỳ khoảng thời gian nào. Bỏ qua khi trống

InterfaceStats: { name: string, rx_bytes: u64, tx_bytes: u64 }


Khung dữ liệu nhị phân

 0        1                    5                    9
 +--------+--------------------+--------------------+
 |  kind  |     session_id     |    payload_len     |  payload (payload_len 字节)
 |   u8   |    u32 (大端)      |    u32 (大端)      |
 +--------+--------------------+--------------------+
 └──────────── 9 字节定长头 ────────────┘
Bù đắp Chiều dài trường Mô tả
0 1 kind Loại luồng; xem bên dưới
1 4 session_id u32 lớn nhất. 0 is reserved as a sentinel meaning no session
5 4 payload_len u32 lớn, số byte tải trọng, tối đa 1 MiB
9 payload_len payload Raw bytes without encoding or escaping

Các loại luồng

Giá trị Tên Hướng Mô tả
1 ExecStdout Thiết bị → nhà điều hành Remote command standard output
2 ExecStderr Thiết bị → nhà điều hành Lỗi tiêu chuẩn, được truyền riêng biệt với thiết bị xuất chuẩn nên CLI có thể hiển thị nó theo cách khác, chẳng hạn như màu đỏ
3 PtyData Bidirectional Luồng byte PTY. Ngược dòng mang đầu vào bàn phím, xuôi dòng mang đầu ra màn hình; cùng loại phục vụ cả hai hướng

Nếu agent nhận được ExecStdout / ExecStderr xuôi dòng, nó sẽ ghi lại cảnh báo và loại bỏ khung: điều này cho thấy lỗi triển khai ở thiết bị ngang hàng.

Ghi chú thiết kế

**payload_len thực sự dư thừa với trường độ dài WebSocket. ** Vẫn còn vì hai lý do: giải mã có thể phát hiện việc cắt bớt (phải từ chối khai báo 11 byte với chỉ 5 byte được cung cấp thay vì chấp nhận âm thầm dưới dạng gói ngắn hơn) và định dạng khung có thể không thay đổi nếu cần TCP thô sau này.

Giới hạn tải trọng 1 MiB không dành cho lưu lượng thông thường: PTY và đầu ra lệnh là các luồng gia tăng nhỏ hơn nhiều so với mức đó. Nó ngăn không cho một máy ngang hàng làm cạn kiệt bộ nhớ máy chủ bằng tiêu đề yêu cầu tải trọng 2 GiB.

Tải trọng Empty là hợp lệ: việc đọc 0 byte đôi khi xảy ra trong các phiên PTY. Khung như vậy chỉ bao gồm tiêu đề 9 byte.

Payload chứa byte tùy ý, bao gồm các chuỗi không phải UTF-8 và phải được bảo toàn chính xác.

Lỗi giải mã

Lỗi Trình kích hoạt
FrameTooShort { len } Total length below 9 bytes
UnknownStreamKind(v) Byte đầu tiên không phải là 1/2/3
PayloadTooLarge { len } payload_len đã khai báo vượt quá 1 MiB
LengthMismatch { declared, actual } Độ dài khai báo khác với số byte còn lại thực tế

Lỗi giải mã khung dữ liệu chỉ ảnh hưởng đến khung đó: exchange ghi lại cảnh báo và loại bỏ nó mà không ngắt kết nối.


Session ID Translation Between Two Namespaces

Đây là phần của giao thức dễ bị hiểu lầm nhất.

Trong Một Câu

initiator phân bổ session_id, initiator chỉ trong kết nối của chính nó. Mỗi kết nối của nhà điều hành và thiết bị đều duy trì các không gian tên ID độc lập và exchange chuyển đổi giữa chúng. Một phiên logic có hai ID khác nhau.

  remote-cli                    exchange                      remote-agent
                                                        (设备 lab-router)

  ┌────────────┐        ┌────────────────────┐            ┌────────────┐
  │ 运维编号   │        │      路由表        │            │ 设备编号   │
  │  空间      │        │                    │            │  空间      │
  │            │        │  lab-router:       │            │            │
  │  session 7 │───────▶│    31 → (op-A, 7)  │───────────▶│ session 31 │
  │            │◀───────│                    │◀───────────│            │
  └────────────┘        │  op-A:             │            └────────────┘
                        │    7 → (lab-router,│
   由 CLI 自己的        │         31)        │             由 exchange
   SessionIdGenerator   └────────────────────┘             的 DeviceHandle
   分配(1,2,3…)                                          分配(1,2,3…)

Tại sao thiết kế này

Avoid a round trip. If the server allocated all IDs, the CLI would have to wait for an acknowledgment saying its session ID is 31 before sending keyboard input after opening a PTY. Phân bổ CLI cho phép các khung dữ liệu ngay sau open_pty.

ATránh xung đột ID giữa các nhà khai thác. Hai nhà khai thác có thể sử dụng đồng thời ID cục bộ 1 trên cùng một thiết bị. Thiết bị phải phân biệt chúng để tránh gửi kết quả đầu ra của người này cho người khác. exchange allocates device-side IDs separately for each device connection, mapping the two session 1 instances to device-side 31 and 32.

Giữ hành vi của người vận hành khỏi ảnh hưởng đến ID phía thiết bị. exchange kiểm soát hoàn toàn ID phía thiết bị; no operator can specify or guess them.

Hai bàn

exchange duy trì một bảng cho mỗi kết nối, với cả hai bảng luôn được cập nhật cùng nhau:

Vị trí Bảng Khóa → giá trị
DeviceHandle.sessions Device-side forward table ID phiên thiết bị → (điều khiển gửi của nhà điều hành, ID phiên của nhà điều hành, ID tài khoản của nhà điều hành, loại phiên, ID hàng kiểm tra)
OperatorHandle.sessions Operator-side reverse index ID phiên vận hành → (ID thiết bị, ID phiên thiết bị)

The reverse index enables cleanup: when an operator disconnects, the server must find and terminate its sessions on every device, otherwise processes and PTYs with no output recipient would remain.

Where Translation Happens

Hướng Tin nhắn Dịch thuật
Operator → device (open_*) Thông báo điều khiển Allocate a new ID in the device namespace, register it in both tables, and use the new ID in the message sent to the device
Người vận hành → thiết bị (pty_resize / close_session) Thông báo điều khiển Tra cứu ID phiên thiết bị thông qua chỉ mục ngược, thay thế và chuyển tiếp
Người vận hành → thiết bị Khung dữ liệu Tra cứu ID phiên thiết bị thông qua chỉ mục ngược và ghi đè 4 byte tiêu đề tại chỗ, giữ nguyên tải trọng
Thiết bị → nhà điều hành Thông báo điều khiển Tra cứu ID phiên của nhà điều hành và kết nối mục tiêu trong bảng chuyển tiếp, thay thế ID và phân phối
Thiết bị → nhà điều hành Khung dữ liệu Tra cứu ID phiên của nhà điều hành trong bảng chuyển tiếp và ghi đè tiêu đề tại chỗ, giữ nguyên tải trọng

Bản dịch khung dữ liệu là bản viết lại only: exchange không phân tích cú pháp, đệm hoặc thay đổi bất kỳ byte tải trọng nào. DataFrame::with_session_id thay thế ID tại chỗ mà không phân bổ lại tải trọng.

Quy tắc phân bổ

Cả hai bộ cấp phát đều tăng từ 1 và skip 0, chẳng hạn như trọng điểm không có phiên được sử dụng trong các thông báo lỗi cấp kết nối.

  • CLI (SessionIdGenerator): gói khi tràn và bỏ qua 0. Sử dụng bốn tỷ phiên trong một kết nối là không thực tế, nhưng gói phù hợp với quy trình chạy dài hơn là hoảng loạn.
  • Thiết bị (DeviceHandle::open_session): sau khi gói, nếu ID thuộc phiên trực tiếp, sẽ tiếp tục đến vị trí trống tiếp theo.

exchange từ chối ID nhà điều hành 0 và sử dụng lại ID phiên hoạt động. Tính hợp lệ của kết nối thiết bị, giới hạn đồng thời và đăng ký trong cả hai chỉ mục đều được kiểm tra trong cùng một phần quan trọng, ngăn chặn phiên mở đồng thời từ nhiều nhà khai thác vượt quá giới hạn.

Khi Không Có Tuyến Đường Tồn Tại

Loại bỏ âm thầm mà không gặp lỗi. Một thiết bị ngang hàng vẫn gửi dữ liệu theo sau ngay sau khi đóng phiên là một cuộc chạy đua bình thường. Điều này áp dụng theo cả hai hướng:

  • Thiết bị trả lời phiên không còn tồn tại → nhật ký gỡ lỗi; nhà điều hành có thể đã ngắt kết nối.
  • Nhà điều hành gửi dữ liệu cho phiên không xác định → nhật ký theo dõi và loại bỏ.

Đường dẫn dọn dẹp phiên

Trình kích hoạt Dọn dẹp
Thiết bị kết thúc phiên bình thường (exec_exit / ubus_resp / android_resp / pty_closed / session_error) Cùng nhau loại bỏ tuyến chuyển tiếp và chỉ mục ngược của kết nối sở hữu, cập nhật bản ghi kiểm tra và gửi thông báo đã dịch cho nhà điều hành
Toán tử close_session Xóa cả hai chỉ mục, yêu cầu kết nối thiết bị ban đầu chấm dứt, ngay lập tức xác nhận việc đóng đối với nhà điều hành và cập nhật bản ghi kiểm tra việc hủy. Đóng liên tục không làm gì cả
Ngắt kết nối thiết bị Thực hiện tất cả các phiên cho kết nối đó, gửi session_failed / device_disconnected cho mỗi nhà điều hành và cập nhật bản ghi kiểm tra với {"outcome":"device_disconnected"}
Nhà điều hành ngắt kết nối Duyệt qua chỉ mục ngược của nó để tìm các phiên của nó trên mỗi thiết bị. Gửi hướng dẫn chấm dứt cho các phiên không giữ; đối với lệnh giữ, chỉ gửi exec_detach, để quá trình chạy. Chỉ dọn sạch các phiên của chính nó; các nhà khai thác khác trên cùng một thiết bị không bị ảnh hưởng
Thiết bị kết nối lại và thay thế kết nối cũ Vô hiệu hóa tất cả các phiên trên kết nối cũ và thông báo cho mỗi nhà điều hành bằng device_disconnected

Chỉ mục ngược liên kết với một kết nối thiết bị cụ thể; nó không chuyển hướng theo ID thiết bị sang kết nối mới sau khi kết nối lại. Nhiều kết nối của nhà điều hành cho một tài khoản, mỗi tài khoản sở hữu một chỉ mục, do đó việc ngắt kết nối một tài khoản sẽ không hủy phiên của kết nối khác.

Xác nhận cho việc hủy rõ ràng: exec trả về exec_exit (code: null, timed_out: false), PTY trả về pty_closed (code: null) và ubus trả về session_failed (reason: device_error). Điều này xác nhận exchange đã đóng phiên chứ không phải quá trình từ xa đã thoát; dữ liệu theo dõi tiếp theo và các thông báo thoát khỏi thiết bị sẽ bị loại bỏ.

Ở giới hạn đồng thời, Exchange trước tiên lấy lại các tuyến mà người vận hành đã không còn hoặc thời hạn đã trôi qua trong cùng một khóa phiên, sau đó kiểm tra lại giới hạn. Thời hạn thực hiện là thời hạn gửi thực tế cộng với thời gian gia hạn 15 giây; tập tin sử dụng 3600 giây cộng thêm 15 giây; các loại khác sử dụng 24 giờ. Giữ người thực thi trong thời hạn của nó sẽ không bị hủy khi người điều hành rời đi. Reclaim/rõ ràng kill_device_session gửi lệnh hủy tương ứng tới thiết bị và ngay lập tức xóa cả hai chỉ mục mà không cần đợi phản hồi của thiết bị. Các nhà khai thác có sẵn nhận exec_exit (timed_out:true) cho người thực thi hoặc session_failed (device_error, session reclaimed) cho các loại khác. Cập nhật kiểm tra là không đồng bộ và nỗ lực tốt nhất.

Kết nối lại thiết bị và kỷ nguyên

Mỗi kết nối thiết bị sẽ nhận được epoch (UUID) ngẫu nhiên trong quá trình đăng ký. Khi cùng một thiết bị kết nối lại, kết nối mới sẽ thay thế kết nối cũ thay vì bị từ chối. Không thể phân biệt một cách đáng tin cậy giữa kết nối trùng lặp với kết nối đã chết mà máy chủ chưa phát hiện được (kết nối nửa mở); từ chối cái mới sẽ ngăn cản việc kết nối lại cho đến khi cái cũ hết thời gian.

Việc dọn dẹp kết nối cũ có thể thực hiện đăng ký after của kết nối mới. Do đó, việc hủy đăng ký phải kiểm tra kỷ nguyên: chỉ xóa mục nhập bảng kết nối nếu kỷ nguyên hiện tại của nó khớp với kết nối đang được dọn sạch. Nếu không có bước kiểm tra này, việc dọn dẹp kết nối cũ sẽ ngay lập tức đánh dấu thiết bị mới được kết nối ngoại tuyến.


Trình tự bắt tay

Phía thiết bị

  remote-agent                                        exchange
      │                                                 │
      │  ①  WebSocket 握手(wss://host/ws/device)      │
      │────────────────────────────────────────────────▶│
      │◀────────────────────────────────────────────────│
      │            101 Switching Protocols              │
      │                                                 │
      │                                          ┌──────┴──────┐
      │                                          │ 启动 15 秒  │
      │                                          │ 注册计时器  │
      │                                          └──────┬──────┘
      │  ②  {"type":"register","protocol_version":1,    │
      │      "device_id":"…","token":"…","info":{…}}    │
      │────────────────────────────────────────────────▶│
      │                                                 │
      │                              ┌──────────────────┴──────────────────┐
      │                              │ a. 比对 protocol_version            │
      │                              │ b. split_token → 前缀查 devices 表  │
      │                              │ c. 校验 enabled                     │
      │                              │ d. argon2 verify(前缀不符也照做,  │
      │                              │    避免用耗时区分前缀是否存在)     │
      │                              └──────────────────┬──────────────────┘
      │                                                 │
      │  ③  {"type":"register_ack","ok":true,           │
      │      "error":null,"heartbeat_interval_secs":30} │
      │◀────────────────────────────────────────────────│
      │                                                 │
      │                                          ┌──────┴──────┐
      │                                          │ 登记连接表  │
      │                                          │ 顶替旧连接  │
      │                                          │ 回写 info 与│
      │                                          │ last_seen_at│
      │                                          └──────┬──────┘
      │                                                 │
      │  ④  退避曲线复位,写状态文件 state=connected     │
      │                                                 │
      │══════════════ 转发循环 ═══════════════════════════│
      │                                                 │
      │  heartbeat(每 30 秒,携带 Metrics)             │
      │────────────────────────────────────────────────▶│
      │  WebSocket Ping(随每次 heartbeat 发出)         │
      │────────────────────────────────────────────────▶│
      │                                     WebSocket Pong
      │◀────────────────────────────────────────────────│
      │                                     WebSocket Ping(每 30 秒)
      │◀────────────────────────────────────────────────│
      │  Pong(tungstenite 排队,agent 显式 flush)       │
      │────────────────────────────────────────────────▶│

Đường dẫn lỗi:

  • Khung đầu tiên không phải là văn bản, JSON không hợp lệ hoặc không phải là register → ghi lại cảnh báo và đóng ngay lập tức mà không cần xác nhận.
  • Không có register trong vòng 15 giây → đóng.
  • Phiên bản không khớp → trả về register_ack{ok:false, error:"协议版本不匹配:设备为 X,服务端为 Y"} và đóng.
  • Mã thông báo không hợp lệ/thiết bị không tồn tại/thiết bị bị vô hiệu hóa → luôn trả về cùng một "设备 ID 或令牌无效" và đóng. DKhông phân biệt nguyên nhân: làm như vậy sẽ khiến kho thiết bị có thể đếm được.

agent xử lý riêng hai loại lỗi này:

Danh mục agent Trình kích hoạt Chiến lược kết nối lại
Rejected Nhận được ack với ok: false Đã sửa lỗi chờ 300 giây. Việc thử lại không thể sửa được mã thông báo không hợp lệ; thử lại thường xuyên gây lãng phí tài nguyên ở cả hai bên và nhật ký máy chủ tràn ngập. Thời gian chờ đợi lâu hơn sẽ giúp ai đó có thời gian cập nhật cấu hình
Transport Lỗi kết nối, hết thời gian bắt tay (45 giây), phản hồi không đúng định dạng, kết nối bị gián đoạn Độ trễ theo cấp số nhân: bắt đầu từ 1 giây, tăng gấp đôi mỗi lần lên tới 300 giây, nhân với jitter ngẫu nhiên trong [0.5, 1.5)

Backoff chỉ đặt lại sau khi đăng ký thành công, không chỉ thiết lập TCP. Nếu không, việc từ chối ngay lập tức sau khi kết nối sẽ chuyển thành số lần thử lại dày đặc trong khoảng thời gian mili giây.

Jitter là điều cần thiết. Khi máy chủ khởi động lại, hàng trăm hoặc hàng nghìn thiết bị sẽ phát hiện tình trạng ngắt kết nối cùng một lúc. Với đường cong thử lại xác định giống hệt nhau, chúng vẫn được đồng bộ hóa và tấn công máy chủ mới khởi động theo từng đợt, biến việc khởi động lại thành một lỗi xếp tầng kéo dài.

Bên điều hành

  remote-cli                                          exchange
      │                                                 │
      │  ①  WebSocket 握手(wss://host/ws/operator)    │
      │      Authorization: Bearer <prefix>.<secret>    │
      │────────────────────────────────────────────────▶│
      │                                                 │
      │                              ┌──────────────────┴──────────────────┐
      │                              │ 在 upgrade 之前完成鉴权:           │
      │                              │ split_token → 前缀查 operators 表   │
      │                              │ → argon2 verify → 校验 enabled      │
      │                              └──────────────────┬──────────────────┘
      │                                                 │
      │      鉴权失败 → HTTP 401「凭证无效」(不升级)   │
      │◀────────────────────────────────────────────────│
      │                                                 │
      │◀────────────────────────────────────────────────│
      │            101 Switching Protocols              │
      │                                                 │
      │  ②  {"type":"welcome","protocol_version":1,     │
      │      "server_version":"0.1.0"}                  │
      │◀────────────────────────────────────────────────│
      │                                                 │
      │  ③  比对 protocol_version(15 秒超时)          │
      │      不一致 → 报错退出,提示升级 CLI            │
      │                                                 │
      │══════════════ 可以发起会话 ═══════════════════════│
      │                                                 │
      │  {"type":"open_pty","session_id":1,             │
      │   "device_id":"lab-router","rows":40,"cols":120}│
      │────────────────────────────────────────────────▶│
      │                                                 │──▶ 查设备在线
      │                                                 │──▶ 查并发上限
      │                                                 │──▶ 查 readonly 角色
      │                                                 │──▶ 写审计(先于下发)
      │                                                 │──▶ 分配设备侧编号
      │  二进制帧(PtyData, session 1, 键盘输入)        │
      │────────────────────────────────────────────────▶│──▶ 改写为设备侧编号
      │                                                 │
      │  二进制帧(PtyData, session 1, 屏幕输出)        │
      │◀────────────────────────────────────────────────│◀── 改写为运维侧编号

CLI có thể gửi khung dữ liệu tiếp theo ** mà không cần chờ bất kỳ xác nhận nào**. Đây chính xác là lý do tại sao người khởi tạo phân bổ ID phiên.


Xử lý lỗi và các loại lỗi

SessionFailure

Được sử dụng trong trường reason của session_failed; Các giá trị JSON sử dụng snake_case.

Giá trị Ý nghĩa Nhà sản xuất Có thể thử lại
device_offline Thiết bị không tồn tại hoặc hiện đang ngoại tuyến exchange không tìm thấy thiết bị nào trong bảng kết nối trong open_* ✅
device_disconnected Thiết bị bị ngắt kết nối trong phiên Thiết bị bị ngắt kết nối/được thay thế bằng kết nối mới/hàng đợi gửi đã bị đóng trong quá trình gửi đi ✅
device_error Thiết bị từ chối yêu cầu hoặc thực thi không thành công agent trả về session_error (tạo PTY, khởi động lệnh hoặc lỗi cuộc gọi ubus) hoặc thiết bị ngang hàng tiêu thụ quá chậm trong quá trình chuyển tiếp ❌
rate_limited Đã vượt quá giới hạn phiên đồng thời exchange nhận thấy các phiên hoạt động của thiết bị đã đạt đến --max-sessions-per-device (mặc định là 32) trong open_* ✅
forbidden Thông tin xác thực của người vận hành không thể truy cập vào thiết bị Tài khoản chỉ đọc (role = readonly) cố gắng bắt đầu bất kỳ phiên nào ❌

is_retryable() cố tình loại trừ forbidden và device_error: việc thử lại vấn đề về quyền hoặc lỗi thiết bị nội bộ là vô nghĩa và chỉ gây ra hoạt động CLI không cần thiết.

CLI thêm một gợi ý có thể thực hiện được sau message ban đầu. Ví dụ: device_offline đề xuất kiểm tra trạng thái thiết bị bằng remote-cli device list. device_error là ngoại lệ: lỗi của chính thiết bị là lời giải thích chính xác nhất và không nên pha loãng bằng bản soạn sẵn.

Ngữ nghĩa lỗi theo lớp

Lớp Hành vi Phạm vi
Lỗi giải mã khung Đăng nhập và loại bỏ khung Một khung hình
Kiểm soát lỗi phân tích tin nhắn Cảnh báo và bỏ qua tin nhắn Một tin nhắn; kết nối tiếp tục
Lỗi cấp phiên session_failed / session_error Một phiên; kết nối tiếp tục
Đăng ký thất bại Đóng sau register_ack{ok:false} Toàn bộ kết nối
Phiên bản không khớp Đóng sau một thông báo lỗi rõ ràng Toàn bộ kết nối
Độ trễ hàng đợi Chủ động đóng kết nối Toàn bộ kết nối

The principle: keep failures local whenever possible, since a device connection may carry simultaneous sessions for multiple operators.


Keepalive và thời gian chờ

Cơ chế Giá trị Mô tả
exchange WebSocket Ping Mỗi --heartbeat-secs (mặc định 30 giây) exchange gửi tới cả thiết bị và nhà điều hành. Thư viện client tự động xếp hàng Pong; agent phải xả sạch bồn rửa sau khi nhận được Ping để gửi Pong kịp thời
agent WebSocket Ping Mọi heartbeat_interval_secs (được cung cấp trong xác nhận máy chủ) agent sends native Ping in addition to each JSON heartbeat, avoiding Cloudflare's approximately 100-second idle-disconnect boundary if exchange Ping is delayed
Hết thời gian im lặng kết nối heartbeat × --missed-pings (mặc định 30 × 3 = 90 giây) Kết nối của thiết bị hoặc nhà điều hành không có tin nhắn gửi đến trong khoảng thời gian này được coi là bị mất và bị ngắt kết nối. Pong được tính là hoạt động; --missed-pings được đặt thành 0 quay trở lại ít nhất một nhịp tim
Hết thời gian ghi exchange 10 giây Bất kỳ dữ liệu WebSocket nào hoặc thời gian chờ ghi Ping đều kết thúc kết nối, ngăn chặn các kết nối chậm chặn quá trình dọn dẹp
Nhịp tim kinh doanh Mọi heartbeat_interval_secs (được cung cấp trong xác nhận máy chủ) agent gửi JSON heartbeat với Metrics và Ping WebSocket riêng biệt. Báo cáo số liệu và bóng bàn/bóng bàn gốc phục vụ các mục đích riêng biệt; riêng nhịp tim doanh nghiệp không thể xác định được trạng thái kết nối
Sự kiện trực tuyến/ngoại tuyến tick_count dành cho trình kích hoạt thông báo online / offline (mặc định 3, phạm vi 1–100); dự phòng phát sóng/kiểm tra REMOTE_MISSED_THRESHOLD / REMOTE_ONLINE_THRESHOLD Người vận hành định cấu hình hai trình kích hoạt, thay vì hai bộ đếm trên một trình kích hoạt. offline thông báo sau khi tick_count liên tiếp bỏ lỡ; online thông báo sau khi tick_count thành công liên tiếp. device_ids của mỗi trình kích hoạt có thể là một ID thiết bị (chuỗi), một bộ ID (mảng) hoặc bị bỏ qua /null cho tất cả các thiết bị không gian làm việc. Trình kích hoạt không lặp lại thông báo khi đã ở trạng thái được báo cáo; đầu tiên nó phải trang bị lại tick_count liên tiếp để có kết quả ngược lại. Kiểm tra và phát sóng hiện diện trong quá trình không đọc các trình kích hoạt này và chuyển về REMOTE_MISSED_THRESHOLD / REMOTE_ONLINE_THRESHOLD (cũng được mặc định là 3). Việc ổ cắm có còn trong bảng kết nối hay không được xác định riêng bởi --missed-pings
Hết thời gian đăng ký thiết bị 15 giây register phải được gửi trong khoảng thời gian này sau khi thiết lập kết nối
Hết thời gian bắt tay agent 45 giây mỗi pha (HANDSHAKE_TIMEOUT) connect_device_ws (TCP, bắt tay HTTP CONNECT, TLS, WebSocket tùy chọn) và chờ register_ack sau khi gửi register, mỗi người đều nhận được giới hạn này; 45 giây không phải là giới hạn tổng hợp của họ
agent ghi/Hết thời gian xả Pong 30 giây (WRITE_TIMEOUT) Mỗi lần gửi (đăng ký, nhịp tim JSON, Ping, đầu ra phiên) và mỗi lần gửi Pong sau đăng ký đều có thời hạn riêng. Lỗi ghi hoặc hết thời gian chờ sẽ kết thúc kết nối và kích hoạt kết nối lại. Pong tuôn ra trong khi chờ đăng ký được điều chỉnh bởi thời gian chờ 45 giây ở trên
agent TCP được giữ nguyên Thăm dò sau 30 giây không hoạt động Cho phép duy trì TCP_NODELAY và TCP sau khi kết nối trực tiếp hoặc KẾT NỐI HTTP; Cài đặt TCP không thay thế WebSocket Ping/Pong
CLI hết thời gian chờ chào mừng 15 giây Thời gian CLI đợi welcome sau khi kết nối
Hết thời gian thực hiện lệnh Được chỉ định bởi timeout_secs trong open_exec agent kẹp ít nhất 1 giây. Khi hết thời gian chờ, SIGTERM toàn bộ nhóm quy trình, sau đó SIGKILL sau khoảng thời gian gia hạn 3 giây
hết thời gian gọi ubus 15 giây (cố định trong agent) ubus là RPC cục bộ; vượt quá khoảng thời gian này thường cho thấy dịch vụ tương ứng bị kẹt

Một client shell nhàn rỗi vẫn trả lời bằng Pong. agent xóa rõ ràng phản hồi được thư viện tự động xếp hàng mà không cần chờ nhịp công việc tiếp theo, do đó việc thiếu đầu vào bàn phím không gây ra ngắt kết nối sai (#42). --heartbeat-secs phải lớn hơn 0; các giá trị không hợp lệ bị từ chối trong quá trình phân tích cú pháp đối số khởi động.


Áp lực ngược

exchange duy trì hàng đợi gửi giới hạn các tin nhắn 512 cho mỗi kết nối.

Hàng đợi đầy có nghĩa là thiết bị ngang hàng không thể theo kịp, thường là do kết nối PTY bị đình trệ hoặc máy khách bị treo. Câu trả lời là ngắt kết nối đó một cách chủ động thay vì tiếp tục lưu vào bộ đệm: việc lưu vào bộ đệm không giới hạn sẽ biến một máy khách bị đình trệ thành rò rỉ bộ nhớ ảnh hưởng đến toàn bộ dịch vụ. Thông báo 512 cung cấp khoảng trống rộng rãi cho lưu lượng PTY tương tác (thông thường độ dài hàng đợi ở dạng một chữ số); đạt đến giới hạn thường có nghĩa là người ngang hàng đã ngừng tiêu dùng.

Phân phối luôn sử dụng try_send không chặn. Đường dẫn định tuyến giữ khóa bảng kết nối; bất kỳ await nào cũng sẽ gắn thời lượng khóa với tốc độ tiêu thụ ngang hàng, cho phép một máy khách chậm làm đình trệ toàn bộ bảng.

Hai lỗi giao hàng được xử lý khác nhau:

kết quả Ý nghĩa Xử lý
Lagging Hàng đợi đầy Ngắt kết nối bằng tín hiệu đóng riêng biệt, dọn sạch các phiên của nó và cố gắng hết sức để thông báo lỗi cho điểm cuối khác
Closed Người nhận phát hành Chấm dứt kết nối bình thường; chỉ nhật ký gỡ lỗi

Tín hiệu đóng kết nối bỏ qua hàng đợi gửi, cho phép dọn dẹp ngay cả khi hàng đợi đầy hoặc quá trình ghi bị chặn. Việc thay thế thiết bị sau khi kết nối lại và xoay vòng mã thông báo sử dụng cơ chế tương tự.

agent cũng có hàng đợi đi có dung lượng 256. Hàng đợi đầy có nghĩa là mạng không thể gửi dữ liệu nào cả, do đó việc tích lũy thêm là vô nghĩa; ngắt kết nối và kết nối lại. Khi hàng đợi đầu vào PTY (64) đầy, các lần nhấn phím sẽ bị loại bỏ: việc tích lũy các đầu vào tương tác nhỏ không có mục đích gì.

Điều khiển màn hình Android

Được xác định trong crates/proto/src/android.rs, sử dụng định tuyến, xác thực, giới hạn đồng thời và kiểm tra phiên hai không gian tên hiện có. request trong open_android / android_req là một đối tượng JSON được phân biệt đối xử bởi action:

action Các lĩnh vực khác Hành vi
status không có Truy vấn phiên bản SDK và ủy quyền cục bộ; không cần bật công tắc điều khiển màn hình nhưng dịch vụ từ xa phải đang chạy
tree không có Cây nút trợ năng của ứng dụng hiện đang hoạt động
screenshot không có Một ảnh chụp màn hình JPEG
tap x, y: u32 Nhấn vào tọa độ pixel màn hình gốc
swipe x1, y1, x2, y2, duration_ms: u32 Vuốt, thời lượng 50–5000 mili giây
swipe_path points: [{x, y}] (2–64 điểm), duration_ms: u32 Vuốt nhiều đoạn: một lần nhấn liên tục đi qua tất cả các điểm, nội suy thống nhất theo độ dài, thời lượng 50–5000 ms
global key: string back / home / recents / notifications / quick_settings
input_text text: string Thay thế trường có thể chỉnh sửa tập trung, tối đa 8192 byte UTF-8, không có NUL

AndroidResult bị kind phân biệt đối xử; tất cả các trường được yêu cầu:

kind Trường
status controls_enabled, accessibility_enabled, screen_capture_enabled: bool; sdk: u32
tree nodes: AndroidNode[]; screen_width, screen_height: u32; truncated: bool
screenshot mime_type: "image/jpeg"; data_base64: string; width, height, screen_width, screen_height: u32
ok Không có; cho biết việc hoàn thành thao tác nhập, cử chỉ hoặc hành động của hệ thống

Các nút chứa các chuỗi class_name, package, view_id, text, description, bounds: [left, top, right, bottom] (tọa độ pixel màn hình có dấu) và booleans clickable, editable, focused, password. Tối đa 200 nút, với mỗi chuỗi giới hạn ở 256 đơn vị UTF-16. truncated chỉ cho biết giới hạn nút. Các giá trị và mô tả mật khẩu trống và cây con cấu hình riêng của agent bị bỏ qua.

Cạnh dài nhất của ảnh chụp màn hình được giới hạn ở 1600 pixel. width/height là kích thước hình ảnh; screen_width/screen_height là kích thước trước khi chia tỷ lệ. Tọa độ cử chỉ sử dụng kích thước ban đầu. JSON được cầu Java trả về bị giới hạn ở 2 MiB, với 1024 byte bổ sung được phép cho đường bao WebSocket. Không có loại khung nhị phân mới nào được thêm vào.

{"type":"open_android","session_id":1,"device_id":"phone","request":{"action":"input_text","text":"Hello\nWorld"}}
{"type":"android_req","session_id":31,"request":{"action":"input_text","text":"Hello\nWorld"}}
{"type":"android_resp","session_id":31,"result":{"kind":"ok"}}
{"type":"android_resp","session_id":1,"result":{"kind":"ok"}}

screen_capture_enabled cho biết ủy quyền ảnh chụp màn hình hiện có sẵn (khả năng truy cập MediaProjection hoặc Android 11+); mọi thao tác trên màn hình thực tế vẫn yêu cầu controls_enabled. Không đủ quyền, cửa sổ không thể truy cập và thao tác không thành công sẽ trả về agent session_error, được Exchange dịch sang session_failed. Các tác nhân không phải Android trả về một lỗi không được hỗ trợ rõ ràng.

Agent xử lý đồng thời tối đa 4 yêu cầu Android và chỉ mã hóa một ảnh chụp màn hình mỗi lần. Các thao tác Java chờ tối đa 10 giây (tối đa 5 giây đối với khung màn hình); Thời gian chờ yêu cầu agent là 15 giây, thời gian chờ CLI là 20 giây. Khi nhà điều hành hủy hoặc ngắt kết nối, Exchange gửi android_cancel; agent đóng ổ cắm cục bộ và APK hủy công việc được xếp hàng đợi chưa chạy. Các cử chỉ hoặc thay đổi văn bản đã hoàn thành sẽ không được khôi phục.

Hành động kiểm tra là android; chi tiết lưu trữ hành động và tọa độ. input_text chỉ lưu trữ characters (số ký tự Unicode) và kết quả thành công chỉ ghi lại outcome: "completed". Nội dung văn bản, cây giao diện người dùng và hình ảnh không được ghi vào hồ sơ kiểm tra. Tài khoản readonly không thể khởi tạo ngay cả status, tree hoặc ảnh chụp màn hình.

Phần mở rộng phiên tệp

Các tác nhân hỗ trợ hoạt động tệp quảng cáo khả năng files. Exchange dịch open_file {session_id, device_id, request} thành file_req {session_id, request}. request.action là list, download hoặc upload; định nghĩa trường có trong crates/proto/src/files.rs.

Thông báo điều khiển hai chiều sử dụng JSON: bộ điều khiển gửi file_input {session_id, input} và thiết bị trả về file_event {session_id, event}. input.kind là chunk {offset, data}, ack {offset} hoặc finish; event.kind là ready {size}, chunk {offset, data}, progress {received}, complete {size} hoặc list {path, entries, truncated}. Nội dung chunk sử dụng tiêu chuẩn Base64, mỗi chunk tối đa là 64 KiB và người gửi chờ xác nhận trước khi tiếp tục.

ready ban đầu của quá trình tải xuống yêu cầu ack {offset: 0}; các đoạn tiếp theo xác nhận số byte tích lũy. Quá trình tải lên bắt đầu bằng ready và mỗi progress sẽ điều khiển đoạn tiếp theo; sau khi gửi mọi thứ, finish cam kết điều đó. complete và list là các tin nhắn đầu cuối; lỗi sử dụng session_error/session_failed hiện có. close_session gửi file_cancel; hủy bỏ và ngắt kết nối đều loại bỏ các tuyến phiên. Tin nhắn tệp chỉ được chuyển tiếp trên các phiên tệp, duy trì sự tách biệt giữa ID phiên của nhà điều hành và thiết bị.

Phiên bản giao thức vẫn là 1; các tin nhắn mới yêu cầu nâng cấp Exchange và Agent phù hợp. Để biết giới hạn hoạt động và giao diện LAN, hãy xem Truyền tệp và Kết nối mạng LAN trực tiếp.

Tiện ích mở rộng đa nền tảng

Phiên bản giao thức vẫn giữ nguyên 1. Đăng ký info bổ sung thêm platform tùy chọn (openwrt / linux / freebsd / netbsd / windows / android / macos), os_version và capabilities (exec / pty / ubus / android_control / files / sensors / net). OpenWrt báo cáo openwrt_version.

Heartbeat metrics.unavailable là một mảng chuỗi tùy chọn xác định các số liệu uptime, load, memory, cpu và interfaces hiện không khả dụng. Nếu vắng mặt, hãy phân tích cú pháp bằng định dạng cũ. Các giá trị tương ứng với số liệu được liệt kê chỉ là phần giữ chỗ tương thích; khách hàng sẽ hiển thị chúng là không có sẵn. Windows không cung cấp mức tải trung bình của Unix và Android có thể hạn chế một số nguồn /proc thông qua quyền hệ thống.

Các lệnh được diễn giải bởi shell của thiết bị mục tiêu: /bin/sh trên Linux/OpenWrt/macOS, PowerShell trên Windows và /system/bin/sh trên Android. Định dạng khung PTY và các thông báo điều khiển giống hệt nhau trên cả ba hệ thống; Windows sử dụng ConPTY. Các thiết bị không phải OpenWrt trả về session_error cho các yêu cầu ubus.

Phiên kín từ đầu đến cuối

Phiên bản giao thức vẫn giữ nguyên 1. Tin nhắn mới: OperatorUp open_sealed {session_id, device_id, hello}, sealed_input {session_id, packet}; Thiết bịDown sealed_open {session_id, hello}, sealed_input, sealed_close {session_id, detach}; DeviceUp / OperatorDown sealed_accept {session_id, accept}, sealed_event {session_id, packet, last}. Loại khung nhị phân mới 4 = Sealed (tải trọng counter ‖ AEAD(inner_kind ‖ bytes)), DeviceInfo.seal_key và khả năng sealed.

Đối với các phiên kín, Exchange chỉ thực hiện dịch ID, giới hạn đồng thời và định tuyến. Nó không phân tích hay kiểm tra tải trọng (hồ sơ kiểm tra chỉ chứa khóa công khai, mức ủy quyền và thời hạn cũng như kết quả cuối cùng) và không quyết định các quyền ngoài việc kiểm tra xem tài khoản có thể nhìn thấy thiết bị hay không. Các tuyến đường được phát hành trên last:true. Các tuyến kín chỉ chấp nhận khung Sealed; các tuyến thông thường từ chối các khung Sealed. agent loại bỏ mọi bản rõ được gửi đến ID phiên được niêm phong. Để biết về ủy quyền, bắt tay, dẫn xuất khóa, mô hình mối đe dọa, hãy xem EPhiên kín từ đầu đến cuối.