Skip to content

Deploying a lab

Packages and extras

Python 3.11 or newer is required. One wheel serves every role; extras select the additional dependencies:

Install Adds Used on
pip install labnet Client library Client computers
pip install "labnet[lab-pc]" FastAPI, Uvicorn, gRPC health Lab PCs
pip install "labnet[central]" FastAPI, SQLAlchemy, Alembic, Uvicorn Central
pip install "labnet[central,postgres]" + psycopg Central with PostgreSQL
pip install "labnet[certs]" cryptography The computer that issues certificates
pip install labnet-control-center FastAPI, Uvicorn, pypdf, the watchdog The Control Center (on the Central computer)
pip install "labnet[driver-dev]" grpcio-tools Writing a driver package (labnet driver build)
uv sync (repository root) Every package, editable, plus pytest and import-linter Development

Production hosts install a reviewed bundle, never an editable checkout. In practice labnet update creates each role's virtual environment and installs the wheels for you.

Production deployment

This walkthrough uses four Windows computers on one private LAN. All addresses are static and all connections use raw IP addresses; no DNS service is assumed.

Computer Static IP Runs Required inbound traffic
development_pc 192.168.50.40 repository clone, labnet-certs, labnet bundle only the chosen file-transfer method
central_server_pc 192.168.50.10 labnet-central behind Caddy TCP 443 from Lab PCs and clients
lab_pc_1 192.168.50.20 labnet-lab-pc TCP 50051 from approved clients
client 192.168.50.30 experiment scripts none

Exclude each address from DHCP, or reserve it for that computer. The certificates contain the server IPs, so changing a server's IP requires a new certificate. Each runtime computer needs Python 3.11 (py -3.11 in PowerShell).

1. Prepare development_pc

Clone the repository and create the development environment:

git clone https://github.com/mill0851/labnet && cd labnet
uv sync

The TLS model

TLS gives two things: encryption on the wire, and proof that a client reached the intended server. LabNet uses the minimum set of certificates for that:

  • One private CA. Its private key labnet-ca.key signs certificates and never leaves the signing computer. Its public certificate labnet-ca.pem is copied everywhere something must be verified.
  • One server certificate per server: central.crt with central.key for Central (read by Caddy), and lab-pc-01.crt with lab-pc-01.key for each Lab PC (read by labnet-lab-pc). Each lists, as its subject alternative name, the exact IP clients dial.
  • No client certificates. Users and Lab PCs prove who they are with API keys, and leases decide what they may do. TLS only secures and authenticates the connection.
Computer Files it holds Why
development_pc (or protected offline storage) labnet-ca.key, labnet-ca.pem Issues certificates
Central central.crt, central.key Caddy presents Central's identity
Lab PC lab-pc.crt, lab-pc.key, labnet-ca.pem Presents its identity; trusts Central
Client labnet-ca.pem Trusts Central and every Lab PC

.pem and .crt files are public. .key files are secret, and only the server that uses a key should receive it. Never copy labnet-ca.key to Central, a Lab PC, a client, a release share, or the repository.

Create the certificates

labnet-certs init-ca --dir C:\LabNet-CA          # prompts for a passphrase for labnet-ca.key
labnet-certs issue central   --ip 192.168.50.10 --dir C:\LabNet-CA
labnet-certs issue lab-pc-01 --ip 192.168.50.20 --dir C:\LabNet-CA
labnet-certs show C:\LabNet-CA\lab-pc-01.crt --ca C:\LabNet-CA\labnet-ca.pem

init-ca creates a ten-year CA. Store its passphrase in the lab's secret manager: losing it makes the CA unusable, and leaking it together with the key lets anyone issue trusted certificates. issue signs a one-year server certificate (--days changes this; repeat --ip/--dns for more names) and refuses to overwrite existing files. show prints the names and validity dates and verifies the signature. Use it before copying anything.

Stage the server files on encrypted removable media or another approved transfer location (for example E:\LabNet-TLS). Remove the server keys from there once deployed. Afterwards, keep C:\LabNet-CA in encrypted offline storage and restore it only to issue or renew a certificate.

On each server, restrict its .key file: open Properties → Security → Advanced, disable inheritance, and leave only Administrators plus the account that runs Caddy or the Lab PC, with read access.

Renewal: issue a replacement with labnet-certs issue <name> --ip … --force, deploy it, and restart Caddy or the Lab PC. The CA stays unchanged. Exposure: if a server key may have leaked, issue a replacement at once. This minimal CA has no revocation list, so an organizational PKI is the better choice when revocation or many identities are needed. If the CA key leaks, create a new CA and redistribute labnet-ca.pem before reissuing every server certificate.

Build the release

# set version = "x.y.z" in every packages/*/pyproject.toml and commit, then:
labnet bundle --site sites/pqt      # --driver <folder> for each driver package; --no-dependencies to use PyPI

The bundle dist/labnet-x.y.z-pqt/ (git-ignored) holds the wheels (LabNet, the Control Center, the drivers, and every dependency), site-pqt.zip (the lab's configuration from sites/pqt/), and MANIFEST.json with the SHA-256 of each file. Copy it to a restricted, read-only share such as \\192.168.50.40\LabNet-Releases\x.y.z. The hashes detect wrong or damaged files; controlling who can write to the share is what keeps them trustworthy.

A wheel is an installable package archive. It does not contain settings, keys, certificates, or databases; labnet bundle refuses a site folder that holds any.

Installing from the bundle

Every computer installs its role with the bundle's install.ps1, which needs only Python 3.11 and its py launcher:

$install = '\\192.168.50.40\LabNet-Releases\x.y.z\install.ps1'
powershell -ExecutionPolicy Bypass -File $install -Role <central|control-center|lab-pc|client>

It checks the bundle, installs labnet from it into a temporary environment, and runs labnet update from there, which refuses while the role runs, builds C:\ProgramData\LabNet\venvs\<role>-x.y.z-<time>, checks it, and only then points C:\ProgramData\LabNet\<role>-venv at it. -ExecutionPolicy Bypass applies to that one run (Windows otherwise refuses unsigned scripts from a share). The steps below use $install from this block.

2. Set up central_server_pc

  1. Certificates. Copy central.crt and central.key to C:\ProgramData\LabNet\certs\.
  2. Settings. Create C:\ProgramData\LabNet\central.env from sites/pqt/central.env.example:
LABNET_DATABASE_URL=sqlite:///C:/ProgramData/LabNet/Central/labnet.db
LABNET_CENTRAL_BIND_HOST=127.0.0.1
LABNET_CENTRAL_BIND_PORT=8008
  1. Install. This verifies the bundle, creates C:\ProgramData\LabNet\central-venv, and creates the database schema.
powershell -ExecutionPolicy Bypass -File $install -Role central
  1. Users and their settings files, in one command:
$admin = "C:\ProgramData\LabNet\central-venv\Scripts\labnet-central-admin.exe"
& $admin --env-file C:\ProgramData\LabNet\central.env setup `
    --admin central-admin --user researcher --lab-pc lab-pc-01 `
    --write-env C:\LabNet-Keys `
    --server-url https://192.168.50.10 `
    --ca-file C:/ProgramData/LabNet/certs/labnet-ca.pem

This writes C:\LabNet-Keys\lab-pc-01.env and researcher.env (and central-admin.env), each with its new API key, Central's URL, and the CA path. Central keeps only key hashes, so these files are the only copy: - move lab-pc-01.env to lab_pc_1 and researcher.env to the client over the secure transfer; - keep central-admin.env for administration; - then delete C:\LabNet-Keys.

Running setup again later only creates missing users.

  1. Start Central by hand in its own PowerShell window, and leave that window open (it listens on loopback only):
C:\ProgramData\LabNet\central-venv\Scripts\labnet-central.exe --env-file C:\ProgramData\LabNet\central.env
Invoke-RestMethod http://127.0.0.1:8008/health/ready
  1. HTTPS with Caddy. Install Caddy and save C:\ProgramData\LabNet\Caddyfile:
https://192.168.50.10 {
    tls C:/ProgramData/LabNet/certs/central.crt C:/ProgramData/LabNet/certs/central.key
    reverse_proxy 127.0.0.1:8008
}

Validate it with caddy validate --config C:\ProgramData\LabNet\Caddyfile, then start it by hand in a second window with caddy run --config C:\ProgramData\LabNet\Caddyfile. Caddy loads the certificate and key with its tls directive and forwards to Central with reverse_proxy. Central's private key never touches the Python process. sites/pqt/Caddyfile.example is the whole file, with the Control Center's site and its logins added (see Control Center).

  1. Firewall. Allow inbound TCP 443 from the Lab PCs and approved clients. Never open port 8008.
  2. Control Center. The web interface runs on this same computer; see Control Center for its setup.
  3. Safe-state watchdog. Applies an Automation grant's safe state if the Control Center dies mid-grant; see The safe-state watchdog for its user, watchdog.env and how to start it.

3. Set up each Lab PC

  1. Certificates. Copy lab-pc-01.crt → C:\ProgramData\LabNet\certs\lab-pc.crt, lab-pc-01.key → …\lab-pc.key, and labnet-ca.pem → …\labnet-ca.pem.
  2. Settings. Rename the transferred lab-pc-01.env to C:\ProgramData\LabNet\lab-pc.env and add the lines from sites/pqt/lab-pc.env.example:
# from setup --write-env
LABNET_LAB_PC_ID=lab-pc-01
LABNET_SERVER_URL=https://192.168.50.10
LABNET_DEVICE_MANAGER_API_KEY=labnet_...
LABNET_CA_FILE=C:/ProgramData/LabNet/certs/labnet-ca.pem
# added for this computer
LABNET_ENVIRONMENT=production
LABNET_GRPC_ADVERTISED_TARGET=192.168.50.20:50051
LABNET_GRPC_CERT_FILE=C:/ProgramData/LabNet/certs/lab-pc.crt
LABNET_GRPC_KEY_FILE=C:/ProgramData/LabNet/certs/lab-pc.key
LABNET_DEVICE_INVENTORY=C:/ProgramData/LabNet/site/inventories/lab_pc_01.py:create_devices

The API key switches on Central registration and lease checks. The certificate and key switch on TLS, with gRPC listening on all interfaces. The advertised target must match the IP in the certificate. 3. Drivers. Install the vendor drivers or SDKs, and test each instrument locally (for example with examples/05_local_hardware.py adapted to its family). The inventory module comes from the repository's sites/pqt/ folder. 4. Install. This installs LabNet, the drivers and the site, then loads lab-pc.env and builds the inventory's devices (without connecting), to catch a mistake or a missing driver immediately.

powershell -ExecutionPolicy Bypass -File $install -Role lab-pc
  1. Start the Lab PC by hand in its own PowerShell window, and leave it open:
C:\ProgramData\LabNet\lab-pc-venv\Scripts\labnet-lab-pc.exe --env-file C:\ProgramData\LabNet\lab-pc.env
Invoke-RestMethod http://127.0.0.1:9001/health/ready

Readiness reports the gRPC listener, Central registration, and each device. 6. Firewall. Allow inbound TCP 50051 from approved clients only. Port 9001 stays on loopback.

4. Set up a client

  1. Copy labnet-ca.pem to C:\ProgramData\LabNet\certs\, and save the transferred researcher.env as C:\ProgramData\LabNet\client.env.
  2. Install the library and the drivers:
powershell -ExecutionPolicy Bypass -File $install -Role client
  1. Point clients at the settings file once (user environment variable), then run the first example (copied from the repository's examples/):
[Environment]::SetEnvironmentVariable("LABNET_ENV_FILE", "C:\ProgramData\LabNet\client.env", "User")
C:\ProgramData\LabNet\client-venv\Scripts\python.exe 01_hello_device.py

The script authenticates with Central at 192.168.50.10, leases a device, and connects directly to lab_pc_1 at 192.168.50.20:50051. One CA file verifies both connections.

Starting the lab by hand

Every process runs in its own PowerShell window that stays open. Start them in this order; to stop one, press Ctrl+C in its window.

Order Computer Window Command
1 central_server_pc Central C:\ProgramData\LabNet\central-venv\Scripts\labnet-central.exe --env-file C:\ProgramData\LabNet\central.env
2 central_server_pc Caddy caddy run --config C:\ProgramData\LabNet\Caddyfile
3 central_server_pc Control Center C:\ProgramData\LabNet\control-center-venv\Scripts\labnet-control-center.exe --env-file C:\ProgramData\LabNet\control-center.env
4 central_server_pc Safe-state watchdog C:\ProgramData\LabNet\control-center-venv\Scripts\labnet-watchdog.exe --env-file C:\ProgramData\LabNet\watchdog.env (or its Task Scheduler task)
5 each Lab PC Lab PC C:\ProgramData\LabNet\lab-pc-venv\Scripts\labnet-lab-pc.exe --env-file C:\ProgramData\LabNet\lab-pc.env
6 clients experiments your scripts, with LABNET_ENV_FILE set

Only Central has to come first; everything else recovers if it starts early. Lab PCs keep retrying their registration, and the Control Center shows a banner until Central answers. Before an update, stop the affected windows; labnet update refuses to run while that role is still running.

Network rules summary

  • Central: allow TCP 443 from approved Lab PCs and client networks.
  • Lab PC: allow TCP 50051 from approved client networks.
  • Never expose Central's port 8008 or a Lab PC's port 9001.
  • Clients need no inbound rule.
  • Where practical, limit outbound traffic: Lab PCs need Central HTTPS; clients need Central HTTPS and the gRPC ports of authorized Lab PCs.

Deployment verification

Verify in this order:

  1. Central /health/live and /health/ready return status: ok through HTTPS (curl.exe --cacert …\labnet-ca.pem https://192.168.50.10/health/ready).
  2. Central lists the expected Lab PC devices.
  3. The Lab PC's loopback readiness reports gRPC serving and devices available.
  4. A client authenticates and lists the expected devices (02_choose_device.py).
  5. The client leases a device and completes one harmless read.
  6. A second client cannot lease the same device at the same time.
  7. Closing the first client releases the lease and frees the device.