We built a complete service-provider core — segment routing, TI-LFA, route reflector, dual-stack L3VPNs — without a single line reaching a device by hand. Every change travelled as code through review, check-mode plan, cryptographic approval and read-only acceptance. This page shows the build as it is: interactive, layer by layer, failures included.
Reference build · next-gen SP core
A provider core.
Every change
signed.
Interactive — switch layers, click routers, explore changes and gates
11
routers
31
signed changes
20
IS-IS adjacencies
7
node SIDs
2
L3VPNs · dual stack
0
ungated applies
What this is
Next-gen does not mean new boxes.
It means a different operating model.
Segment routing instead of an accreted protocol stack, precomputed repair paths instead of convergence hope, a route-reflector control plane instead of a full mesh, dual stack as a service property instead of a special project — that is the technical side of what distinguishes modern provider networks. It is fully implemented here and explorable below, layer by layer.
The real point, though, is the way there: 31 changes, each a versioned file, each with its own check-mode plan, immutable manifest, independent Ed25519 approval and read-only acceptance. 15 of those changes stopped fail-closed — and precisely that is the quality mark: no failed plan was ever reused, no gate ever loosened, no device ever touched “quickly by hand”.
The standard
An accepted commit is no proof. A change counts as delivered only once operational acceptance proves it — end to end, from the customer's perspective, with regression of every existing service.
The topology
Seven layers, one network.
Every layer of this topology is really configured and individually accepted. Switch through the planes and click individual routers — the values are the real ones: loopbacks, SIDs, RDs, route targets.
11 routers · 2 provider edges · 5 core nodes (1 of them a route reflector) · 4 customer edges
Roles
The role split of a provider network: R1 and R6 terminate customers, R2–R5 and R7 carry nothing but labels, and R7 additionally reflects the VPN routes. Seven IOS XR systems form the core, four IOS systems the customer sites.
- PE = provider edge: holds VRFs and customer sessions
- P = provider core: pure MPLS transport, no customer state
- RR = route reflector: VPN control plane without a forwarding role
- CE = customer edge: the customer-site device
Click a router for details
Failure drill
The outage as a proof
Everyone claims resilience. Here it was demonstrated: a core link detached under control, the repair path observed live, the restore as its own signed change. Replay the drill — step by step.
Step 1 / 4
BaselineRedundant steady state
All 20 adjacencies up, TI-LFA has precomputed a repair path for every link. Both L3VPNs and the route reflector run green.
Service status during the drill
- R7 Route Reflector — green
- CUST-A IPv4 — green
- CUST-B IPv4 + IPv6 — green
The change journey
31 changes. 15 of them fail-closed.
Exactly as it should be.
This list is unfiltered — the failures are here too, because they are the evidence that the gates bite. Every red entry stopped serially at the first problem, was archived immutably and replaced by a new, corrected change. Not once was a gate loosened to “get through”.
16 completed · 15 fail-closed · 31 of 31
One roadmap stage — model-driven telemetry — deliberately waits: the telemetry collector rejected authentication before a single command ran. No credentials were guessed; the stage stays deferred until its gate is green.
The change path
Ten stages. Fail-closed.
Every edge of this path is a gate that stops hard instead of warning. Select a stage for details — or test the path against tampering: the simulation shows which gate catches which deviation.
01 · Change as code
Every change is a versioned file with a declared scope: target hosts, maintenance window, prechecks, validation, rollback. Exactly one intent file per device, holding candidate configuration and verification conditions.
- changes/CHG-NNNN.yml (schema-validated)
- intent/…/CHG-NNNN/HOST.yml per target
- Rollback is its own inverse change, never a config replace
Fail-closed probe
What happens if you cheat?
Pick a manipulation. The pipeline runs up to the gate that detects it — and stops there. No exception, no override button.
Separation of duties
Seven separate identities via Keycloak OIDC: author writes, reviewer merges, operator plans, approver approves, auditor reads, automation-bot orchestrates — and the break-glass account stays disabled until a documented emergency change opens it. Nobody approves their own change; the rule lives in the cryptography, not the org chart.
The artifacts, verbatim
No mock-ups: these excerpts come from the real change CHG-0026 — the intent, its verification conditions and the structure of the manifest binding.
Intent (excerpt, TI-LFA on R1)
network_candidate_config: - "router isis CORE" - " interface GigabitEthernet0/0/0/0" - " address-family ipv4 unicast" - " fast-reroute per-prefix" - " fast-reroute per-prefix ti-lfa"
Verification as data (same intent)
network_verify_commands: - show isis neighbors - show isis segment-routing label table network_verify_conditions: - result[0] contains 'R2' - result[1] contains '16001'
Manifest binding (structure)
change: CHG-0026 · sha256:… intents: 7 files · sha256:… commit: c90961f… hosts: R1,R2,R3,R4,R5,R6,R7 (+IP/Port/OS) plan_log: sha256:… image_id: sha256:… (immutable)
Protocols & configuration
What actually runs in the network.
No name-dropping — every line here is configured, verified and part of the running regression. The parameters are the real ones.
Underlay
- IS-IS “CORE” · level 2 only
- Area 49.0001 · wide metrics
- P2P adjacencies · Loopback0 passive
A single, flat IGP as the foundation. No OSPF area design, no redistribution — the simplicity is the operational decision.
Label transport
- LDP: auto-configured from IS-IS + IGP sync
- SR-MPLS: SRGB 16000–23999, SIDs 1–7
- Coexistence without sr-prefer
Two label planes in parallel: LDP carries the present, segment routing the future — migrated without a flag day, both verified independently.
VPN control plane
- MP-BGP AS 1 · VPNv4 + VPNv6
- RR 7.7.7.7 with IPv4 RT constraint
- RTs 1:100 / 1:200 proven operationally
Route-reflector design instead of a full mesh, RT constraint instead of scatter traffic — and the migration there without a second of control-plane gap.
Customer services
- CUST-A: IPv4 L3VPN, RT 1:100, VLAN 100 handoff
- CUST-B: dual stack via 6VPE, RT 1:200
- as-override · dedicated export prefix lists
Two isolated L3VPNs on shared infrastructure. IPv6 arrives via 6VPE across the IPv4 core — the customer gets dual stack, the core stays lean.
Management hardening
- SSHv2 · RSA-2048 · login local
- Host keys verified byte for byte
- transport input ssh — Telnet removed
No trust on first use: every host key is compared against independent evidence before automation trusts it. Legacy crypto is scoped per host and hashed into the approval manifest.
Gated next stages
- EVPN multihoming · SRv6 / FlexAlgo
- VPWS on a supported platform
- Model-driven telemetry · gNMI only with a CA
The roadmap is honestly gated: every stage needs a proven forwarding plane, not just an accepting parser. Streaming telemetry only starts with a trustworthy PKI — there is deliberately no insecure fallback.
The toolchain
Open tools,
one closed path.
Open source throughout, every component with a clear job and a clear trust boundary: images pinned by digest, ports on loopback only, secrets never in logs. None of the surfaces can write to a device past the signed path.
Execution
The path that actually touches devices — pinned, containerised, serial.
- Ansiblenetwork_cli via ansible-pylibssh, strict host-key checking
- Cisco Collectionscisco.ios, cisco.iosxr, cisco.nxos — version-pinned
- Execution Environmentansible-builder image, digest-bound in the manifest
- ansible-navigatorRuns inside the EE instead of on the host
- uv · Python 3.12Reproducible tool environment with a lockfile
- Docker · WSL 2Unprivileged containers, read-only root, tmpfs runtime
Control & identity
Who may do what — as configuration, not as an agreement.
- KeycloakOIDC for every surface; seven separate role identities
- ForgejoGit, pull requests, protected main, CODEOWNERS logic
- Forgejo RunnerRepository-scoped, its own TLS DIND — never the host socket
- AWXWorkflow plan → approval → apply → verify with a real waiting node
- OpenBaoTransit attestation of the approval, declarative audit log
Evidence & observability
Traceability as a system property — without scattering raw data.
- ARAPlaybook, task and host history — without task contents or secrets
- NetBoxSource of truth with a reconcile gate; authoritative only after zero drift
- OxidizedRedacted configuration history as a Git diff per change
- Prometheus · GrafanaPlatform and telemetry metrics via the verified SSH path
- Loki · AlloyCentral logs, secret patterns redacted before shipping
- Evidence-IndexSHA-256 per artifact; verifiable offline without any GUI
Quality gates
Fourteen check classes before every merge — with zero device access.
- Ruff · Mypy · PytestPython quality, types and tests with coverage
- ansible-lint · yamllintPlaybook and data discipline
- Gitleaks · BanditSecret detection and Python security
- pip-audit · TrivyDependency and image vulnerabilities, tightly scoped exceptions with expiry dates
- actionlint · ZizmorThe CI workflows themselves are under test
- Hadolint · ShellCheckContainerfile and shell hardening
- CycloneDXSBOM per build — the supply chain is documented
Next step
The same method, your network.
Provider core or enterprise campus: the change path — intent as code, plan, independent approval, verified acceptance — transfers. We will show you what it looks like in your environment, on one concrete standard change.
Request an assessmentAssessment → first dependable change

