Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
713 changes: 685 additions & 28 deletions Cargo.lock

Large diffs are not rendered by default.

23 changes: 22 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ page_size = "0.6"
# Protocol serialization
postcard = { version = "1.1.3", features = ["use-std"] }

# ADR-0009 browser transport. The dependency remains optional so minimal
# native-only builds can opt out with `--no-default-features`.
saorsa-transport = { version = "0.35.3", features = ["webrtc-direct"], optional = true }
self_encryption = { version = "0.36", optional = true }

[target.'cfg(unix)'.dependencies]
libc = "0.2"

Expand Down Expand Up @@ -166,8 +171,13 @@ name = "poc_price_floor_live"
path = "tests/poc_price_floor_live.rs"
required-features = ["test-utils"]

[[test]]
name = "webrtc_direct_devnet"
path = "tests/webrtc_direct_devnet.rs"
required-features = ["webrtc-direct"]

[features]
default = ["logging"]
default = ["logging", "webrtc-direct"]
# Enable tracing/logging infrastructure.
# Included in `default` so dev builds (`cargo build`, `cargo test`) get logging
# automatically. Release builds strip it:
Expand All @@ -176,6 +186,17 @@ logging = ["tracing", "tracing-subscriber", "tracing-appender"]
# Expose test helpers (cache_insert, payment_verifier accessor) for
# integration tests and downstream test harnesses.
test-utils = []
# Direct browser transport from ADR-0009. Enabled by default; minimal
# native-only builds can omit it with `--no-default-features`.
webrtc-direct = [
"dep:saorsa-transport",
"dep:self_encryption",
]

[patch.crates-io]
ant-protocol = { git = "https://github.com/WithAutonomi/ant-protocol.git", rev = "4dad14b6947b6264e0b5982c976a385f9fdac9e0" }
saorsa-core = { git = "https://github.com/WithAutonomi/saorsa-core.git", rev = "2ed691c7cb49690c86beb445ee422f22a230ef9a" }
saorsa-transport = { git = "https://github.com/WithAutonomi/saorsa-transport.git", rev = "2c6e23cd6cd4db79f475676e25af9334a044a343" }

[profile.release]
lto = true
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,10 @@ let harness = TestHarness::setup_with_evm().await?;
assert!(harness.anvil().is_healthy().await);
```

For the direct-browser testnet, where every node exposes WebRTC Direct and a
default immutable file is published at startup, see
[Browser-enabled local testnet](docs/WEBRTC_DIRECT_TESTNET.md).

### Roadmap

| Phase | Target | Status |
Expand Down
6 changes: 6 additions & 0 deletions assets/browser-devnet-public.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Hello from an Autonomi browser-enabled local testnet.

This immutable file was published into node storage when ant-devnet started.
The web application discovers its BLAKE3 address from the browser manifest,
performs the closest-node lookup itself, downloads the bytes directly from a
storage node over WebRTC Direct, and verifies the content address in-browser.
169 changes: 169 additions & 0 deletions docs/WEBRTC_DIRECT_TESTNET.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Browser-enabled local testnet

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-web-support` repository.

## Start the node testnet

Rust 1.88 or newer is required by the Saorsa WebRTC Direct transport.

```bash
cargo run --bin ant-devnet -- \
--preset minimal \
--base-port 23000 \
--webrtc-direct \
--webrtc-direct-base-port 24000 \
--serve-port 25000 \
--enable-evm \
--enable-logging
```

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. The browser manifest
contains only public RPC/token/vault configuration and never contains the
key.
If `HELLO.payment.rpc_url` shows `https://arb1.arbitrum.io/rpc`, the devnet was
started without local Anvil; stop it and restart with the command above.

## Start the browser client

In `ant-client-web-support/web`:

```bash
npm ci
npm run dev
```

Open `http://127.0.0.1:5173`. The app automatically loads the browser manifest.
To upload, choose a file, paste the funded private key printed by ant-devnet,
and use **Pay and upload file**. 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 key field is cleared immediately. The
result address is placed into the download field automatically.

Use **Download and save file** 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 **Prepare video stream** 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.

## Automated verification

```bash
cargo test --test webrtc_direct_devnet -- --ignored
```

This 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.

## LAN testing

Use `--host <LAN_IPV4>` to advertise the literal LAN address:

```bash
cargo run --bin ant-devnet -- \
--preset minimal \
--host 192.168.1.50 \
--webrtc-direct \
--serve-port 25000 \
--enable-evm \
--enable-logging
```

Expose the client dev server on the LAN with `npm run dev -- --host 0.0.0.0`
and change its manifest URL to
`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.

## Public Internet smoke testing

The standard `ant-node` build now includes and enables WebRTC Direct, so the
sibling `ant-testnet` tool needs no browser-specific preset or flags. On its
ordinary public droplets, a node maps its native UDP port deterministically
into the existing allowed UDP 32768-65535 range and advertises the external IP
learned by the native transport (falling back to the host's routed IP). Its
persisted DTLS certificate keeps the complete address stable across restarts.

Deploy the normal testnet against this checkout, for example:

```bash
cd ../ant-testnet
python3.11 testnet.py \
--saorsa-node-repo ../ant-node-web-support \
deploy
```

`ant-testnet` always keeps bootstrap droplets public. Read node 0's canonical
address using its existing shell command, without modifying the deployment
tool:

```bash
python3.11 testnet.py shell --droplet 0
cat /var/lib/ant/node-0/webrtc-direct.multiaddr
exit
```

Start `ant-client-web-support/web`, paste that address into the demo, and use
**Connect and use as bootstrap**. 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`. A minimal binary can omit the transport
entirely with `--no-default-features`.

Each node publishes its certificate-pinned WebRTC Direct multiaddress through
Saorsa's extensible V2 address plane as transport `WebRtcDirect`, independently
of its reachability class. Its signed identity capability selects V2 when the
remote peer supports it; older peers continue receiving the unchanged V1
`Quic` address projection. `FindNodeV2` returns browser endpoints separately
from QUIC addresses, and the browser verifies the peer-ID and certificate
binding during HELLO.
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.

On 2026-08-27 this path was exercised against the normal 60-node testnet from
one bootstrap address. Headless Chromium traversed multiple independent nodes,
obtained four storage quotes from four non-bootstrap closest nodes, submitted
one payment, and stored all four encrypted records successfully. 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.
Loading