Skip to main content

Neops Remote Lab

PyPI Python CI Docs Status: developer preview

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 · Docs · Worker SDK consumes it as a stable contract

Install

For the Python client / pytest fixture (library install) — pick whichever your project uses:

uv add neops-remote-lab        # uv (recommended)
poetry add neops-remote-lab    # Poetry
pip install neops-remote-lab   # pip

For the runnable server CLI (isolated install):

uv tool install neops-remote-lab   # uv (recommended)
pipx install neops-remote-lab      # pipx

Picking between the two? Library install is what most consumers want — it gives you the pytest fixture and RemoteLabClient. CLI install is for operators standing up the server itself.

Quick start

Use it (consumer)

export REMOTE_LAB_URL=http://lab.example.com:8000
from neops_remote_lab.testing.fixture import remote_lab_fixture

demo = remote_lab_fixture("tests/topologies/demo.yml")
def test_lab_has_two_devices(demo):
    assert len(demo) == 2
pytest -v

The fixture handles session creation, queue waiting, topology upload, heartbeat, and teardown. The Worker SDK imports remote_lab_fixture directly as a stable public API.

Run it (operator)

neops-remote-lab            # binds 0.0.0.0:8000 by default
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000/healthz
# 204 = alive

netlab must be on PATH — the launcher refuses to start otherwise. For a fresh host, see Netlab host setup; for a systemd-managed deployment, see the Operator runbook.

How a session flows

sequenceDiagram
    participant ClientA as Client A
    participant ClientB as Client B
    participant Server

    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)
    Note over Server: acquire lab (netlab up)
    Server-->>ClientA: 200 lab acquired

    loop heartbeat (until release)
        ClientA->>Server: POST /session/heartbeat
        Server-->>ClientA: 204
    end

    ClientB->>Server: GET /session/{id}
    Server-->>ClientB: status=WAITING, position=1

    ClientA->>Server: DELETE /session/{id}
    Note over Server: tear down lab, promote next in queue
    Server-->>ClientA: 204

    ClientB->>Server: GET /session/{id}
    Server-->>ClientB: status=ACTIVE, position=0

The full state machine, heartbeat cadence, and timeout semantics live on Session queue; SHA-256-keyed reuse semantics on Lab lifecycle.

Why use it

  • One lab per host, enforced. Cross-process filelock + reference counting; two clients can't trample each other's topology. Topology identity is the SHA-256 of file content, so byte-identical files share a running lab.
  • Stable Python contract. remote_lab_fixture is the public surface the Worker SDK imports directly.
  • Driveable from any HTTP stack. pytest is convenient; cURL works; Go works; whatever you already have works.
  • Operator-friendly. Single-instance systemd unit, structured logs, /healthz and /debug/health, automatic recovery from stale Netlab instances on startup.

Environment variables

Variable Purpose Default
REMOTE_LAB_URL Base URL for client + fixtures — (required)
REMOTE_LAB_REQUEST_TIMEOUT Per-HTTP-request timeout (seconds) 30
REMOTE_LAB_SESSION_TIMEOUT Client-side session-queue wait limit (seconds) 600
REMOTE_LAB_ACQUISITION_TIMEOUT Max wait for lab acquisition (seconds) 600

Timeout-coordination guidance + RemoteLabClient constructor overrides: Client config.

Network access

The service ships without HTTP authentication — the only access boundary on /lab/* is the X-Session-ID of an active session. Treat it as internal-trust; deploy behind a network enclosure. The recommended path is self-hosted Headscale + Headplane because that's what the project's reference deployment uses, but any equivalent enclosure works (managed Tailscale, plain WireGuard, an internal VLAN with IP allowlists, mTLS at a reverse proxy). Walkthroughs and alternatives: Headscale: quick setup and its Other approaches section.

Troubleshooting

Symptom Likely cause + fix
Server exits with Another Remote Lab Manager instance is already running. Stale single-instance filelock from a crashed prior run. See Stale-lock recovery.
Address already in use on port 8000 Prior server didn't exit cleanly, or another service holds the port. lsof -i :8000 and kill, or start with --port.
POST /lab returns 423 Locked Caller's session isn't ACTIVE, or a different topology owns the host. GET /session/{id} to check; release / force-destroy if stuck.

Full table: Debugging.

Documentation

The full docs are at docs.neops.io/neops-remote-lab. Common entry points:

  • Use from Python — pytest fixtures, RemoteLabClient, client config.
  • Run the service — install, systemd, security model, REST contract, debugging.
  • Deploy & Operate — Netlab host setup, VPN paths, vendor walkthroughs (FRR / SR Linux / IOL).
  • Concepts — architecture, session queue, lab lifecycle, topology format.
  • Cookbook — runnable end-to-end recipes.

Interactive OpenAPI UI is at http://<host>:8000/docs once the server is running.

Tests

make test                     # 39 tests; uses a stubbed LabManager (no Netlab needed)

Contributing

Branch from develop. make check runs lint, typecheck, audit, and tests. Full contributor guide: Contributing.

License

See the Developer Preview Disclaimer at the top of this file. A formal open-source license will be applied once finalized.

Metadata

Release files for neops-remote-lab 1.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for neops-remote-lab 1.4.2
File Size Uploaded Provenance Actions
neops_remote_lab-1.4.2.tar.gz 25.8 kB Without provenance Details for neops_remote_lab-1.4.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for neops-remote-lab 1.4.2
File Interpreter ABI Platform Actions
neops_remote_lab-1.4.2-py3-none-any.whl Python 3 none any Details for neops_remote_lab-1.4.2-py3-none-any.whl

Total release size: 56.3 kB

Release files / neops_remote_lab-1.4.2.tar.gz

Download URL neops_remote_lab-1.4.2.tar.gz
Size 25.8 kB
Tags Source
SHA-256 checksum
How to use checksums
d855120116205cebd7480d39b7620372abb0608b3f5415ca7b777a5470e74ded
BLAKE2b-256 checksum
How to use checksums
d92efcc6950292ad31be22ed07e0ffb33dd73884ea802bff7deb4328bce5f209
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / neops_remote_lab-1.4.2-py3-none-any.whl

Download URL neops_remote_lab-1.4.2-py3-none-any.whl
Size 30.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5106817e5b3d954dad47dbee6d88b4b4baa727e91e1ba68fd0a09cf304bd9800
BLAKE2b-256 checksum
How to use checksums
d814918929c46391e7e14790dbd1b6076ea2bb036f55bccce0aa3edc451307f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for neops-remote-lab 1.4.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page