HubN’s peering portal can take two autonomous systems from a request to a set of BGP and tunnel configurations. It supports BIRD2, FRR, VyOS and Pathvector, direct links, WireGuard and IPsec, bilateral peering and several transit relationships.
It also used to be tested mostly one layer at a time.
The frontend tests knew that the right text appeared. The Registry tests knew that an allowed request became a session. Individual router snippets passed their native parsers. None of that proved that two routers using the exact text copied from the portal would establish a session, exchange the intended routes and reject the routes they shouldn’t receive.
I built a separate end-to-end lab to answer that. It found enough problems that “the generated config parses” is no longer a milestone I find particularly comforting.
Starting at the Request Button
The test doesn’t call a template function and declare the output good. It logs into the actual portal through Authentik, impersonates one of the dedicated test handles, creates a peering request, switches to the other side, accepts it and completes the setup.

A request in the test portal. The tier rules, relationship direction and available transports are part of the test, not fixture text pasted around the API.
Once a session exists, Playwright visits it as both parties and captures every platform tab from the rendered DOM. The raw steps are retained, hashed and assembled with only the ordinary daemon boilerplate needed to make a complete configuration.

The generated configuration is copied from the same page a participant would use. The tests do not replace it with a cleaner lab version.
BIRD2, FRR and Pathvector configurations are checked by their native implementations. VyOS gets its own isolated validator container; the test loads the generated set commands as a candidate and then discards it. After that, a dedicated pair of lab routers receives the configuration and has to do something useful with it.
There is a 24-router topology running beside the isolated pair as a control. The test routers don’t borrow hand-written route policy from it, but the collector can observe the resulting sessions in the same way it observes the rest of HubN.
The peering test matrix
The annoying part about supporting four routing platforms is that every relationship has an orientation. A BIRD provider with an FRR customer is not the same generated path as an FRR provider with a BIRD customer. Bilateral sessions remove some ordering, but transit adds it straight back.
The direct matrix ended up with 94 portal-created cases and 3,290 checks. It covers the required tier relationships and every relevant platform pairing. A separate negative matrix tries the relationships which the tier policy should refuse through both the UI and the Registry API.
WireGuard adds 12 positive cases and 656 checks. The generated configuration has to produce a handshake, keep peer routes out of the main table where intended, establish BGP, pass payload traffic, survive endpoint roaming and recover after either router restarts.
The negative cases are at least as useful. They try addresses outside the allocated prefix, malformed and doubled CIDRs, repeated addresses across active sessions and documentation-only endpoints which must parse but never form a tunnel. The one-view PSK exchange is also driven through the browser. The Registry relays encrypted material, while the generated configuration only contains a placeholder for the secret link rather than the key itself.
IPsec has eight cases and 332 checks across native VyOS VTI configuration and route-based XFRM on the other routers. The lab checks which side initiates, whether exactly one SA pair appears, BGP and payload state, and restart recovery.
Then there are the routing services around the session. The RPKI matrix moves routes through Valid, NotFound, Invalid and back to Valid without restarting BGP. The IRR tests change AS-SET membership, run the generated update jobs, check atomic reloads and make WHOIS fail to ensure the old filters remain in place.
By the time the complete run closed, it covered the portal, Registry, Looking Glass, WHOIS, ROA service, route collector and the four router implementations. There were no manual policy edits in the passing run.
What Broke
The early failures were not exotic. Some were exactly the sort of small template mistake which slips through when the person writing the generator also reads its output:
- one path could produce an IPv6 host address with
/128/128; - provider and customer roles were reversed in parts of the generated guidance;
- tunnel addresses were syntactically valid but not proven to belong to the ASN configuring them;
- a pending collector session could disappear from the portal before it had a chance to establish;
- IRR updater variants disagreed about source names and stale-list cleanup;
- some community actions were documented but not preserved across the customer and provider policy boundary.
The last one caused the most work. The first complete run passed, and a later production handoff audit showed that the functional large-community actions had not been exercised deeply enough. I reopened the test closure instead of treating the existing report as sacred.
The corrected run checks local-preference requests, no-export, one/two/three-AS prepends, strict RPKI, remote-triggered blackholing, RFC 7999 and the controls which should remain inside the T1 mesh. All eight functional cases are created through the portal and use its rendered policy. The additional 104 assertions now pass across BIRD2, FRR, VyOS and Pathvector.
That was a useful reminder that a large test count can still contain a very precisely shaped hole.
A Session Has Several Kinds of State
One outcome of the lab is that the portal no longer treats an active Registry session as proof that the network is active.
There is workflow state: both parties accepted the relationship and supplied the necessary details. There is configuration state: the portal can render complete input for the chosen platform. There is transport state: the direct link or tunnel actually passes traffic. Then there is BGP and policy state: the session is established, the collector sees it, and the received routes have the expected validation and communities.
The page can show those separately. A session waiting for a router is not failed merely because the collector has not seen it yet. A configured tunnel with no handshake is not active merely because both users filled in the form.
The distinction also made the next bit of work possible. HubN already had peerings which were established before this workflow existed. They appeared in the Looking Glass, but the Registry knew nothing about them.
Previously: Making HubN Registry Updates Durable.