This workflow starts a five-node local Autonomi network where every node has a
direct WebRTC Direct endpoint. Startup publishes a default immutable test file
and serves browser bootstrap metadata; the companion site lives in the sibling
ant-client-browser-sdk repository, with Rust/WASM from ant-client-web-support.
Rust 1.95 or newer is the shared native/WASM baseline. The startup and transport policy is recorded in ADR-0015.
cargo run --bin ant-devnet --features test-utils -- \
--preset minimal \
--base-port 23000 \
--webrtc-direct \
--webrtc-direct-base-port 24000 \
--serve-port 25000 \
--enable-evm \
--enable-loggingtest-utils explicitly enables development-only prepaid file seeding. Without
it, the listeners and ordinary paid uploads still work, but manifests contain
files: []; an explicit --public-file is rejected before startup. Production
builds do not expose the prepaid cache insertion API.
The services are:
| Purpose | Address |
|---|---|
| Native node QUIC | UDP 127.0.0.1:23000-23004 |
| Direct browser WebRTC Direct | UDP 127.0.0.1:24000-24004 |
| Native devnet manifest | http://127.0.0.1:25000/api/devnet-manifest.json |
| Browser bootstrap manifest | http://127.0.0.1:25000/api/browser-manifest.json |
| Manifest service metadata | http://127.0.0.1:25000/api/info |
| Local Anvil JSON-RPC | printed at startup (random loopback port) |
When --serve-port is omitted with --webrtc-direct, port 25000 is used. Pass
--public-file /path/to/file to replace the built-in
autonomi-browser-testnet.txt. The generated default is 5 MiB so the demo
necessarily reconstructs multiple storage records. A custom file may be up to
1 GB (1,000,000,000 bytes) in this local in-memory launcher. The practical
limit depends on the browser having enough available memory.
The browser manifest contains every node's self-contained WebRTC Direct
multiaddress, with its certificate SHA-256 multihash and peer ID embedded,
plus the public DataMap address, plaintext file hash, and resolved
reconstruction metadata. The HTTP server provides bootstrap metadata only;
the DataMap and file bytes are read from storage nodes over WebRTC Direct.
Each address string is serialized directly from saorsa_core::MultiAddr; the
node does not maintain a browser-specific multiaddress codec.
--webrtc-direct requires an explicit payment network. For this local test,
--enable-evm starts Anvil and startup prints a Funded wallet private key. This
is a disposable local Anvil key for browser upload testing. HELLO and the browser
manifest contain only chain_id, payment_token_address, and
payment_vault_address. Local Anvil uses chain ID 31337. Neither the verification
RPC URL nor the funded key is included in browser metadata.
Browser protocol v5 and browser manifest v6 require matching node, Rust/WASM
client, and SDK versions. The application or wallet owns its payment provider;
no browser RPC setting is needed on the node. For a custom EVM network, the node
privately resolves eth_chainId from its verification RPC when starting the
browser listener. Failure to resolve the chain ID fails listener startup.
Built-in Arbitrum networks use their known chain IDs. Clients compare payment
chain and contract identities, so they can use a different provider for the same
network.
In ant-client-browser-sdk:
npm ci
ANT_CLIENT_DIR=../ant-client-web-support npm run sync:wasm
npm run devOpen the URL printed by Vite. Paste a node's WebRTC Direct multiaddress from the browser manifest and click Connect. To upload, choose a file, paste the Anvil JSON-RPC URL and funded private key printed by ant-devnet, and use Pay and upload. The page self-encrypts locally, verifies node quotes, signs the approval/payment locally, and sends only encrypted records and public payment proof to nodes. The result address is placed into the download field automatically.
Use Download and save to fetch the public DataMap and every encrypted file chunk directly, reconstruct the complete file, validate its whole-file BLAKE3 hash, and save it under its original filename.
For a browser-supported video, use Stream as media and then the native video controls. The Rust/WASM reader fetches and decrypts only records overlapping the media element's requested byte ranges. A same-origin service worker provides standard HTTP range responses locally; no file bytes pass through the manifest server or another gateway.
cargo test --locked --test webrtc_direct_devnet --features test-utils -- --include-ignoredThis starts Anvil and the five-node network, self-encrypts and publishes a
default public file through normal PUT admission with devnet-prepaid cache
entries, extracts a generated certificate pin from the advertised
multiaddress, retrieves and reconstructs it, then obtains a real signed quote,
pays it on-chain, uploads a fresh record through paid PUT_CHUNK, and reads it
back through WebRTC Direct.
The same suite checks that encrypted HELLO and browser manifests omit a custom verification URL containing dummy credentials and API keys, and that invalid chain-ID responses fail without exposing provider details.
Use --host <LAN_IPV4> to advertise the literal LAN address:
cargo run --bin ant-devnet --features test-utils -- \
--preset minimal \
--host 192.168.1.50 \
--webrtc-direct \
--serve-port 25000 \
--enable-evm \
--enable-loggingExpose the client dev server on the LAN with npm run dev -- --host 0.0.0.0
and obtain a bootstrap address from
http://192.168.1.50:25000/api/browser-manifest.json. Both the native and
WebRTC Direct UDP ranges must be reachable. Do not use this unsigned local
manifest mode on a public network.
The standard ant-node build includes and enables WebRTC Direct. An enabled
listener's configuration, payment-network, certificate, or bind failure aborts
node startup. Use --disable-webrtc-direct (or
ANT_DISABLE_WEBRTC_DIRECT=true) to disable it explicitly.
Like native QUIC, port 0 asks the OS to choose an available UDP port; an
explicit --webrtc-direct-port remains fixed. Wildcard listeners advertise a
same-family non-relay IP from the canonical native address view. They wait for
usable native address discovery, refresh the published endpoint as it changes,
and never fabricate a route-probe or loopback fallback. The endpoint file is
created only once an address is known.
Like native QUIC, a wildcard IPv4 listener without an advertised address, on a node outside development mode, also asks the local gateway for a UPnP mapping of its port, and advertises the mapped public address while the gateway holds it (ADR-0027). Without a gateway, or when it refuses, the endpoint is the native view's as above. When the mapping ends and the native view yields no address, the mapped endpoint is withdrawn rather than left advertised. Peers stop seeing it even if the endpoint file cannot be removed; the node retries the file on each refresh. An explicit bind IP or advertised address is never mapped.
Configure an explicit WebRTC port and permit inbound UDP on that port when a
stable bootstrap endpoint or firewall rule is needed. Retain the node identity
and certificate and use a fixed public IP. Certificate persistence alone does
not keep an OS-assigned port stable. An explicit
--webrtc-direct-advertised-addr can override the public IP and external port
without a bind override. The development launcher above uses explicit port
ranges separately for its in-process nodes.
Deploy the normal testnet against this checkout, for example:
cd ../ant-testnet
python3.11 testnet.py \
--saorsa-node-repo ../ant-node-web-support \
deployant-testnet always keeps bootstrap droplets public. Read node 0's canonical
address using its existing shell command, without modifying the deployment
tool:
python3.11 testnet.py shell --droplet 0
cat /var/lib/ant/node-0/webrtc-direct.multiaddr
exitStart ant-client-browser-sdk, paste that address into the demo, and use
Connect. The operation installs the single address as
the Rust browser client's seed without DNS or a browser manifest. The address
contains only the public DTLS certificate hash and ANT peer ID; it contains no
secret key material. To disable the listener in a custom node configuration,
set webrtc_direct.enabled = false or use --disable-webrtc-direct. A build
with --no-default-features omits the listener stack; it still uses the same
pinned native core and protocol dependencies. This is not a dependency rollback.
Public listeners apply an independent resource envelope; the native QUIC limits are not shared with browser traffic. The defaults are:
| Setting | Default | Scope |
|---|---|---|
max_connections |
256 | listener |
max_connections_per_ip |
4 | IPv4 address or IPv6 /64 |
max_channels_per_connection |
2 | association |
max_channels |
512 | listener and channel-handler tasks |
max_concurrent_requests |
512 | listener work slots |
max_requests_per_second |
256 | listener work token bucket |
max_requests_per_second_per_ip |
32 | source-prefix work token bucket |
max_requests_per_second_per_connection |
16 | association work token bucket |
max_in_flight_bytes |
64 MiB | listener frame memory |
max_in_flight_bytes_per_ip |
16 MiB | source-prefix frame memory |
JSON request headers have a fixed 64 KiB protocol limit shared with the browser client; it accommodates paid quotes with full signed commitments and is not configurable. Complete plaintext frames are limited to 5 MiB + 64 KiB before JSON parsing. The JSON object is followed directly by raw binary content.
The per-IP ceilings must remain strictly below their corresponding global ceilings. The product of the per-IP connection and per-connection channel limits must also remain below both global channel and request concurrency. Invalid combinations fail node startup instead of silently removing the headroom reserved for other clients. Native IPv6 sources share a /64 quota, and IPv4-mapped IPv6 shares its IPv4 quota. The transport's pre-association admission and application budgets use the same source-key function. Rate buckets permit a one-second burst; overload closes the offending channel or association without queueing more handler tasks. PQ handshakes consume the same work slots and rate tokens as RPCs, and response writes use size-scaled deadlines so slow readers release their reservations.
Each node publishes its certificate-pinned WebRTC Direct multiaddress through
Saorsa's extensible V2 address plane as transport WebRtcDirect, independently
of its reachability class. Native peers send both signed V2 records and the
unchanged V1 QUIC projection; native publication does not negotiate a version
through identity capabilities. Older peers process V1 and ignore the separate
V2 topic. FindNodeV2 forwards original owner-signed records. The browser
verifies these proofs before dialing and verifies the peer-ID and certificate
binding during the PQ session and HELLO. V2 uses nonempty replacements with
no withdrawal operation or routine expiry. The browser RPC addr-v2 capability
requests these records and is independent of native publication.
Consequently one pasted address is enough to enter the network and discover
the browser endpoints of closest peers across independently deployed
processes. Native QUIC dialing ignores the supplemental transport entry.
The 2026-08-27 public smoke run used the former protocol v3 and headless Chromium only. From one bootstrap address it traversed multiple independent nodes, obtained four storage quotes from non-bootstrap closest nodes, submitted one payment, and stored all four encrypted records. It is historical connectivity evidence, not v5 or cross-browser acceptance evidence. Nodes behind the testnet's deliberate inbound-NAT rules remain unreachable without relayed WebRTC, so their 10-second DataChannel timeouts currently make this smoke path slower than an all-public fleet.
The web-support branches use exact Git revisions for development. Cargo removes
[patch.crates-io] overrides from a packaged crate and replaces versioned Git
dependencies with registry dependencies. A successful branch build therefore
does not establish that the crate can be published.
Before releasing ant-node with WebRTC, merge and publish the dependency changes in this order (the first two can be released independently):
For each release, choose a new version through the normal release process.
Raise downstream dependency minimum versions to the releases that contain the
required APIs and features, including version fields on Git dependencies. Remove
the corresponding development Git pins and registry patches once those releases
are available, then regenerate the lockfile and run cargo publish --dry-run
with normal compilation verification. Repeat in dependency order.
In particular, published transport 0.36.3 does not contain webrtc or
webrtc-direct; core 0.27.3 and protocol 2.3.5 also precede the web-support
changes. Their existing version requirements must not be treated as the minimum
compatible web-support releases. Merely removing the patches or adding a new
version number locally cannot supply the missing registry code.
The node Release workflow now runs package verification for stable-version dry
runs as well as real releases. Dry runs never upload a crate. This check must
pass against crates.io before tagging the node release; cargo package --list
and builds using local patches are not substitutes. Prereleases retain the
existing binary-only release behavior.
Tracked under V2-803.