Node network protocol

This page is generated from the protocol specification, services/nodes/spec.py, the same data served at /nodes/spec.json. The server, the clients, the tests and this page all read that one source, so the numbers below cannot drift from the code.

Status today: the specification and /nodes/spec.json exist. The node socket, the device registration, the consent flow and the clients are not implemented yet. Nothing on this page is enabled for any account.

Rules that never change#

  • Not an open proxy. open_proxy is false. A node only ever contacts hosts on the allowlist below. Changing that is a protocol change, not a setting.
  • Web ports only. Ports 80 and 443 and nothing else.
  • The donor enforces it too. A client refuses a request that breaks these rules even when the server asks for it, so a compromised server cannot aim a donor's machine at their own network. The refusal codes are listed below.
  • Opt-in. A node never connects unless the account holder granted the consent. Withdrawing it disconnects every node of that account.
  • No cookies, no accounts. A node never attaches the donor's cookies or logins to a request.

Version#

Protocol version 1, oldest supported 1. A client announces its version in hello; an unsupported version is closed with code 4400.

Transports#

Mode Used by Who can read the content Trust
tunnel native clients nobody on the way: TLS runs end to end between DevPlace and the destination, through the donor's socket tampering is impossible
fetch browser extension the donor can read and could forge the response verified by structure checks and sampled redundancy, labelled fetch in results

Frames#

Every frame is one JSON object with a type. An unknown type, a missing required field, an unexpected field or an oversize frame closes the socket with a reason. Direction c2s is client to server, s2c is server to client.

Type Direction Max bytes Fields
hello c2s 262144 protocol: int, client_version: str, kind: str, platform: str, modes: list, limits: object (optional)
pong c2s 262144 none
job_result c2s 3145728 job_id: str, status: int, headers: object (optional), body_b64: str, truncated: bool (optional), elapsed_ms: int (optional)
job_error c2s 262144 job_id: str, code: str
tunnel_data c2s 262144 conn_id: str, b64: str
tunnel_close c2s 262144 conn_id: str
tunnel_opened c2s 262144 conn_id: str, ok: bool, code: str (optional)
usage c2s 262144 bytes_up: int, bytes_down: int
welcome s2c 262144 device_uid: str, limits: object, destinations: list, settings: object
ping s2c 262144 none
job s2c 262144 job_id: str, method: str, url: str, headers: object (optional), timeout: int
tunnel_open s2c 262144 conn_id: str, host: str, port: int
tunnel_data s2c 262144 conn_id: str, b64: str
tunnel_close s2c 262144 conn_id: str
settings_update s2c 262144 settings: object
kick s2c 262144 reason: str

Close codes#

Code Meaning
4013 this worker does not own the node socket, reconnect immediately
4029 the network is at its connection capacity
4400 protocol error or unsupported version
4403 kicked: consent withdrawn, device revoked or suspended
1008 policy: not authenticated
1009 frame too large

Limits#

Every number is a hard ceiling. Nothing is unbounded and nothing autotunes.

Limit Value
max_devices_per_user 5
max_connected_nodes 2000
max_frame_bytes 262144
max_body_bytes 2097152
max_concurrent_per_node 2
min_seconds_per_node_domain 6
job_timeout_seconds 25
max_queue_depth 500
default_wait_seconds 15
max_wait_seconds 60
default_daily_requests_user 200
default_daily_requests_guest 0
default_donor_mb_per_day 500
max_donor_mb_per_day 20480
ping_seconds 20
missed_pings_before_evict 2
reconnect_min_seconds 1
reconnect_max_seconds 60
ticket_ttl_seconds 30
cache_ttl_seconds 600
cache_max_bytes 268435456
cache_entry_max_bytes 4194304
usage_daily_retention_days 400
event_retention_days 30
event_max_rows 50000
request_retention_seconds 600
xp_daily_cap 50
dispatcher_tick_ms 200

Destination allowlist#

Host suffix Adapter Methods Ports Modes Max requests per second
reddit.com reddit GET 80, 443 tunnel, fetch 0.5
redd.it reddit GET 80, 443 tunnel, fetch 0.5

A host matches a suffix when it equals it or ends with . plus the suffix. evilreddit.com and reddit.com.evil.net do not match reddit.com.

Refusal codes#

A client reports one of these in job_error when it declines a job.

Code Meaning
host_not_allowed destination is not on the allowlist
port_not_allowed port is not 80 or 443
address_not_public resolved address is private, loopback, link-local, CGNAT or multicast
redirect_not_allowed redirect leaves the allowlist
method_not_allowed method is not permitted for the destination
bandwidth_exhausted donor daily bandwidth cap reached
concurrency_exceeded donor concurrency cap reached
outside_hours outside the donor's active hours
body_too_large response exceeds the body cap
timeout job exceeded its timeout

Donor-side checks#

Before any connection a client resolves the host itself, refuses private, loopback, link-local, CGNAT and multicast addresses, then connects to the address it resolved. It follows no redirect that leaves the allowlist. It applies the donor's own bandwidth, concurrency and schedule limits locally, even when the server sends larger ones.