Live macOS test against the production server uncovered six bugs (one of which
turned out to be a port collision with sing-box, not a real bug); this commit
addresses all of them and adds v3.4 port discovery so the same collision is
handled transparently next time.
## v3.4 server port-discovery
- Defaults moved off 443/444 to 8443/8443/8444 (TransportSection::default,
ServerInitOpts, ProvisionClientOpts, CLI flags). 443 is heavily contested in
practice (sing-box, Hysteria2, reverse proxies) and the previous default
silently lost the bind when a co-tenant was already there.
- MultiServer::bind_with_outer_or_scan: scans forward up to
DEFAULT_PORT_SCAN_MAX (20) candidates per transport when the requested port
is occupied; QUIC keeps walking if it lands on the custom-UDP port.
- MultiServer::bound_addrs(): the actual addresses each transport bound to.
- Server logs the bound addresses and writes a runtime snapshot
(server.toml.runtime.json) when they differ from the requested ones, so
`aura sign-bridges` can re-sign the bridges manifest later.
- BridgeManifest gains an optional `endpoints: Vec<BridgeEndpoint>` field
with per-transport ports. Backward-compatible: old v3.3 clients ignore the
field and continue to use the v1 `bridges` line.
- `aura sign-bridges --endpoints HOST:tcp=N:quic=N:udp=N` to mint v3.4
manifests; bridges line is auto-synthesised for v3.3 clients.
## Bug fixes from the live test
- macOS TUN naming (#41): the tun crate rejects names that don't match
^utun[0-9]+$. On macOS we now substitute `""` (kernel auto-assigns utunN),
capture the assigned name via inner.tun_name(), and propagate it through to
os_routes::OsRouteGuard::install — so `route add -interface utunN` uses
the real interface, not "aura0".
- Packet counters (#42): Stats { tx_packets, rx_packets } are now actually
bumped by the data path. `aura status` shows live numbers instead of
permanent zeros.
- render_client_toml schema (#44): provisioner emits proper
`[[tunnel.split.vpn]] cidr = "..."` / `[[tunnel.split.direct]]` blocks from
new --vpn-cidrs / --direct-cidrs flags. The v3.3 `vpn_cidrs = [...]` flat
array was silently ignored by serde, leaving users with `rules: 0` even
when their CIDRs looked right.
- #43 / #46 (TCP/443 dial early-eof / no payload back): diagnosed as the
sing-box port collision, not an Aura bug. The v3.4 port-scan path makes it
go away — the server picks a free port and clients learn it from the
manifest.
## Test coverage
Three new unit tests for the port-scanner (UDP busy, TCP busy, zero budget);
two new tests for v3.4 BridgeManifest round-trip with endpoints; one
integration test for the new `[[tunnel.split.vpn]]` rendering; tests for the
runtime-state file write/read round-trip; agent-added router-counter tests
in aura-tunnel/tests/routes.rs.
cargo test --workspace, cargo clippy --workspace -- -D warnings, and
cargo fmt --check all pass.
#45 (silent client exit when underlying QUIC transport breaks) is still
outstanding — needs deeper investigation; deferred to a follow-up.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Server now assigns each connected client an IP from a configurable pool and
maintains a client_ip -> AuraConnection map so packets read from the shared
TUN are dispatched to the right client (and each client's recv loop writes
back to the TUN). Removes v1's "single shared TUN, no NAT/pool" limitation;
turns the server into a proper multi-client VPN concentrator (paired with the
already-landed UDP multi-client demux).
- aura_cli::pool: IpPool + PoolStrategy {StaticOnly, DynamicOnly,
StaticOrDynamic}; reserves network/broadcast/server-own IP; 15 tests.
- aura_cli::server_router: ServerRouter + ServerRoutes (Arc<RwLock<HashMap>>);
central TUN read loop dispatching by dst_ip; spawn_inbound_forwarder per
conn auto-unregisters and releases the IP on disconnect; 4 tests via
MockTun + MockConn.
- aura_cli::config: [server.pool] {cidr, strategy, static} added with
serde(default); legacy configs (only [tunnel] pool_cidr) fall back to a
DynamicOnly pool (backward compatible, tested).
- aura_cli::server: accept loop now: pool.assign(peer_id) -> register ->
spawn_inbound_forwarder; rejected static_only mismatches dropped+logged.
- config/server.toml.example: documented [server.pool] section.
Workspace: 141 tests passed (+24), clippy -D warnings clean, fmt clean. No
new workspace deps (async-trait added to cli dev-deps for mock traits in tests).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>