Neops Remote Lab
A FastAPI service exposing exclusive, queue-brokered access to a real Netlab topology — drive it from a pytest11 plugin (Python), the bundled REST API (any stack), or both.
neops-remote-lab fronts a Netlab host with a small HTTP service and a pytest fixture. Every consumer asks for a session, waits in a FIFO queue, gets the lab, and tears it down when the last consumer walks away. Topologies are identified by their SHA-256 content hash, so byte-identical files share the running lab — and FRR, Nokia SR Linux, and Cisco IOL all work out of the box.
On PyPI · Source · Worker SDK uses it as a stable contract
Pick your path
-
Worker SDK developers
A real Netlab device for your function-block tests.
- Install +
REMOTE_LAB_URL— two steps, then the Worker SDK testing guide takes over - Multi-vendor topology patterns (FRR, Nokia SR Linux, Cisco IOL)
- The pytest fixture the Worker SDK imports as a stable API
- Install +
-
Operators
Run the service on a shared host.
- 30-second foreground launch +
/healthzsanity check - Pick the deployment shape — laptop, VM, multi-tenant, or CI runner pool
systemdunit, stale-lock recovery, stuck-lab cleanup runbook
- 30-second foreground launch +
-
Contributors
Change the codebase safely.
- The 30-minute path to your first PR
- Eight invariants every change must preserve
- Internals deep-dives (async, locking, atexit, test stubbing) and an anti-patterns grep target for review
-
External API users
Drive the lab from any HTTP-capable stack.
- Six cURL calls end-to-end — no Python required
- Full REST contract with per-endpoint examples
- Wire into GitHub Actions, GitLab CI, or Jenkins
Session-and-lab lifecycle
Every test follows the same four-step lifecycle: create a session (which joins the queue), wait to become active, upload a topology and acquire the lab, then release on teardown. Client B waits behind Client A without ever touching a lock.
sequenceDiagram
participant ClientA as Client A (pytest)
participant ClientB as Client B (CI job)
participant Server as FastAPI server
participant Manager as LabManager
participant Netlab
ClientA->>Server: POST /session
Server-->>ClientA: 201 session_id (ACTIVE)
ClientB->>Server: POST /session
Server-->>ClientB: 201 session_id (WAITING, position=1)
ClientA->>Server: POST /lab (topology.yml)
Server->>Manager: try_acquire(topo, reuse=True)
Manager->>Netlab: netlab up topology.yml
Netlab-->>Manager: nodes
Server-->>ClientA: 200 lab acquired
loop heartbeat every <300s
ClientA->>Server: POST /session/heartbeat
end
ClientB->>Server: GET /session/{id}
Server-->>ClientB: status=WAITING, position=1
ClientA->>Server: DELETE /session/{id}
Server->>Manager: release() ref--
Note over Server: teardown, promote next
Server-->>ClientB: (next poll) status=ACTIVE, position=0
FIFO queue → exclusive access → shared teardown when the last user leaves. The promotion rules, stale-session timeouts, and heartbeat cadence live in Session Queue; SHA-256-keyed reuse counting in Lab Lifecycle.
Where it sits in the neops ecosystem
neops-remote-lab is the test substrate for the wider neops platform. The Worker SDK imports remote_lab_fixture directly. If you’re new to neops, How Remote Lab fits with neops maps the surrounding pieces; if you’re not on neops at all, this section is safely skippable.
No HTTP authentication
neops-remote-lab ships without bearer-token, OAuth, or mTLS
authentication. The only access boundary on /lab/* is the
X-Session-ID of an ACTIVE session. Deploy behind a VPN. See
Security model for the full posture.
Reading paths
Pick the route that matches your current question.
New to the project — you want your first passing test
Quickstart → Pytest Fixtures → Topology Format. Install, three-line test, see it pass.
Concepts first — you want to understand before you build
Architecture → Session Queue → Lab Lifecycle → Topology Format. Every invariant the system enforces and why.
Standing up the host — you are deploying the service
Netlab host setup → Headscale quick start → Operator runbook → Configuration. Install Netlab, enclose the host in a private tailnet, configure and operate the server.
Wiring in a new client — you are integrating a consumer
REST API → Python Client → Pytest Fixtures. The API reference is authoritative; the Python client is a thin wrapper; the fixture is the stable consumer surface.
Driving from a non-Python stack — you are integrating into an existing harness
REST quickstart → CI quickstart → Debugging. Stand up a session and a lab end-to-end with cURL, then wire it into your CI runner of choice.
Looking for runnable examples
Cookbook. Pytest, Python (no-pytest), cURL, topology, and deployment recipes — every link goes to GitHub so the recipe survives docs-site rebuilds.
External references
- neops platform docs — the umbrella site for the wider platform.
- Netlab — the upstream lab orchestrator this service wraps. Authoritative reference for topology YAML, providers, and vendor kinds.
- Worker SDK — the primary consumer; imports
remote_lab_fixtureas a stable API (integration guide). - Containerlab — the container runtime Netlab drives by default in this project (
provider: clab). - Headscale — the open-source Tailscale control plane used for the recommended VPN enclosure (deployment guide).
- Material for MkDocs reference and pymdown-extensions Snippets — theme and extension docs backing this site.
- Project
README.mdandAGENTS.md— repository-level conventions, invariants, and agent context.