92 lines
5.3 KiB
Markdown
92 lines
5.3 KiB
Markdown
# Deterministic topology harness
|
|
|
|
Issue #14 is verified at three layers. The layers are deliberately separate so
|
|
the always-on gate remains deterministic while privileged CI workers can add a
|
|
stronger operating-system topology without overstating what local emulation
|
|
proves about the public Internet.
|
|
|
|
## Always-on public-process gate
|
|
|
|
`TestClientProcessIntegrationTests` launches the built server and the same
|
|
`FinalFactory.Rendezvous.TestClient` executable shipped to operators. Every
|
|
child process uses `--script --json`, dynamic HTTP and UDP ports, bounded
|
|
state-driven waits, and enforced process-tree cleanup.
|
|
|
|
The suite proves:
|
|
|
|
| Scenario | Required observation |
|
|
| --- | --- |
|
|
| Three-party happy path | register, presence-ready, browse, authorize, punch, authenticated LiteNetLib connection, direct ping/echo/ack/completion traffic, outcome report, disconnect, deregister |
|
|
| Same-LAN candidate | the connected peer is reported as `loopback` or `private`, never inferred merely from an introduction callback |
|
|
| Empty and missing selection | browse exits `11`; exact missing lookup exits `10` |
|
|
| Wrong tenant/protocol | no listing is returned for an incompatible protocol; exact joins with either mismatch fail before `join.punch` |
|
|
| Traversal timeout | an unreachable mediator produces typed `PunchTimedOut`, exits `12`, advertises the configured dedicated fallback, and never connects to it |
|
|
| Caller cancellation | POSIX `SIGINT` exits `130`, deregisters the listing, and removes it from public lookup |
|
|
| Abrupt host loss | the listing disappears after the presence window and before its lease expires; public exact lookup intentionally reports `NotFound` |
|
|
| Bounded host without a peer | exits `13` and still deregisters |
|
|
|
|
Captured output is parsed as the stable JSON v1 event schema. Publisher
|
|
credentials and signing-key material are checked against all captured output.
|
|
The direct traffic payload is handled only by the caller-owned host and client
|
|
LiteNetLib managers; the HTTP service and mediator do not implement or observe
|
|
the echo protocol.
|
|
|
|
Run the always-on scenarios with:
|
|
|
|
```bash
|
|
dotnet test Rendezvous.slnx --configuration Release --no-build \
|
|
--filter FullyQualifiedName~TestClientProcessIntegrationTests
|
|
```
|
|
|
|
## Deterministic protocol and adverse-state gate
|
|
|
|
The following real service-boundary tests cover conditions that a public CLI
|
|
cannot safely manufacture by accepting raw capabilities or tickets:
|
|
|
|
| Scenario | Test evidence |
|
|
| --- | --- |
|
|
| Same-NAT private candidates | `NatMediationProcessorTests.MatchedPeersReceiveOneIntroductionAndSameNatPrivateCandidates` |
|
|
| Separate observed endpoints | `NatMediationProcessorTests.DifferentNatsAndInvalidLocalClaimsExposeOnlyObservedPublicEndpoints` |
|
|
| One-time introduction and replay | `InMemoryEphemeralRendezvousStoreTests.AttemptCapabilitiesAndIntroductionAreOneTime` |
|
|
| Direct ticket replay | `RendezvousCoordinatorIntegrationTests.CallerOwnedManagersCompleteAuthenticatedDirectConnectionAndRejectTicketReplay` |
|
|
| Wrong tenant/protocol and stale presence | `InMemoryEphemeralRendezvousStoreTests.JoinRequiresExactScopeProtocolAndFreshHostPresence` |
|
|
| Cancellation and late callbacks | `RendezvousCoordinatorBehaviorTests.CancellationCompletesExactlyOnceAndLateCallbacksCannotReopenTheAttempt` |
|
|
| Mediator restart | both cases of `UdpMediatorServiceTests.NativeLiteNetLibRequestsIntroduceTheAuthorizedPair`; the restarted case rebinds the same UDP port and completes a native LiteNetLib introduction |
|
|
|
|
These tests use fake monotonic clocks or state predicates where expiry and race
|
|
ordering matter. They do not use fixed sleeps as proof of state.
|
|
|
|
## Privileged Linux namespace gate
|
|
|
|
When a Linux CI worker can create network namespaces, the workflow sets
|
|
`RENDEZVOUS_RUN_NETNS_TESTS=1` and reruns
|
|
`PrivilegedLinuxNatNamespacesCompleteDirectTrafficAcrossSeparateObservedEndpoints`.
|
|
The test creates a temporary WAN bridge, an isolated service namespace, two NAT
|
|
router namespaces, and isolated host/client LAN namespaces. Each NAT has its own
|
|
inside subnet and WAN address. Linux forwarding plus per-router MASQUERADE rules
|
|
force the service to observe separate translated endpoints; the public TestClient
|
|
processes must then complete authenticated direct traffic through those mappings
|
|
using the public candidate. Namespaces, rules, veth pairs, bridge, processes, and
|
|
sockets are removed in bounded async-disposal paths. A cleanup failure fails the
|
|
test.
|
|
|
|
If `ip netns add`/`iptables` is unavailable or the worker lacks `CAP_NET_ADMIN`,
|
|
CI records the limitation and keeps the always-on loopback suite as the required gate.
|
|
To request the privileged run explicitly:
|
|
|
|
```bash
|
|
RENDEZVOUS_RUN_NETNS_TESTS=1 dotnet test Rendezvous.slnx \
|
|
--configuration Release --no-build \
|
|
--filter FullyQualifiedName~PrivilegedLinuxNatNamespacesCompleteDirectTrafficAcrossSeparateObservedEndpoints
|
|
```
|
|
|
|
## What this does not prove
|
|
|
|
Loopback, MASQUERADE, and namespace routing cannot reproduce every consumer router,
|
|
carrier-grade NAT, firewall, IPv6 transition mechanism, symmetric NAT mapping,
|
|
or real-world packet-loss pattern. The separate-observed-endpoint processor
|
|
test proves that untrusted private claims are excluded and public candidates are
|
|
selected; it is not presented as universal Internet traversal proof. Real
|
|
network canaries and measured production readiness remain the scope of issue
|
|
#23.
|