Dashboard access

Last updated on October 14, 2026

A procedure that declares a dashboard: block runs with the credentials of the identity its run uploads with. A phase or a plug that names dashboard as a parameter receives a ready-made tofupilot.v2.TofuPilot client; any other code in the procedure reads the same credentials from the environment. dashboard is the TofuPilot API, acting as this station.

Use it when a test needs workspace state as an input: reserving a serial number by creating a unit, checking that a previous run of a unit passed, looking up a sub-unit, writing metadata on another unit. Uploading the run itself, its measurements and attachments needs none of this; the framework does it.

Declare it

procedure.yaml
name: Label printer
dashboard:
  identity: station
FieldValuesMeaning
identitystation (default), anystation refuses to start the run unless it acts as a station. any also accepts a user login, for development.

Without the block the procedure receives no credentials at all, whatever is set on the machine.

In a phase

phases/allocate_serials.py
def allocate_serials(measurements, dashboard, unit, log):
    station = dashboard.stations.get_current()
    dashboard.units.create(
        serial_number="AAAVTL26410001",
        part_number="ENS0000120",
        revision_number="A",
        metadata={"label_status": "pending"},
    )
    log.info(f"Reserved on {station.name}")

dashboard is injected by name, like log or unit. The client is built once per worker process and shared by its phases. Every method of the Python SDK is available; the server applies the identity's permissions exactly as for a direct SDK call.

For editor completion, annotate the parameter. The framework ignores annotations:

from tofupilot.v2 import TofuPilot

def allocate_serials(measurements, dashboard: TofuPilot, log):
    ...

In a plug

A plug whose __init__ declares dashboard receives the same client. It cannot be set from config:.

plugs/serial_registry.py
class SerialRegistry:
    def __init__(self, dashboard, mode="api"):
        self._client = dashboard

A scope: station plug keeps the client across runs. When the station is logged in again (tofupilot login --token issues a new key), the next run respawns held plugs with the new key.

Which identity

The procedure acts as the identity the run uploads with. A run that does not upload has no identity: a phase asking for dashboard fails with that explanation.

RunIdentity
Station daemon, kiosk, tofupilot run --deploymentThe station
tofupilot run . --upload on a machine logged in as a stationThe station. With identity: station, the station login is used even when a user login is also present.
tofupilot run . --upload on a machine with a user login onlyThe user, with identity: any. Refused at start with identity: station.
tofupilot run . without --uploadNone

What the procedure may call is what the identity may call. A station creates and reads units, parts, revisions and batches, reads and updates them, and reads its own linked procedures and runs; see access roles. Permissions are managed in the dashboard, not in the procedure.

In the environment

Every process the framework starts for the procedure (phase workers, plug processes, shell phases, and anything they spawn) has these variables. Code that builds its own client, or a shell phase, reads them directly.

VariableValue
TOFUPILOT_API_KEYThe run's API key
TOFUPILOT_URLThe instance base URL, without /api (the value tofupilot login --url takes)
TOFUPILOT_CA_CERTPath of the private CA bundle, only on an instance that uses one
Your own client
import os
from tofupilot.v2 import TofuPilot

client = TofuPilot(server_url=os.environ["TOFUPILOT_URL"] + "/api")  # key read from the environment
A shell phase
main:
  - name: Count units
    executable:
      command: >
        curl -sS -H "Authorization: Bearer $TOFUPILOT_API_KEY"
        ${TOFUPILOT_CA_CERT:+--cacert $TOFUPILOT_CA_CERT}
        "$TOFUPILOT_URL/api/v2/units?limit=1"

The key is the station's own. Do not write it to a file or a log; the framework sets it in the environment on every run.

Self-hosted and private CA

Nothing to configure in the procedure. The dashboard client trusts the instance's private CA on top of the default bundle, so the procedure's other HTTPS calls are unaffected. Code that builds its own client loads TOFUPILOT_CA_CERT into its own SSL context.

When the network is down

Calls are not queued. A call made while the server is unreachable raises the SDK's transport error; a server error raises a TofuPilotError with its status. A phase that needs a write to succeed must fail or retry; a phase that continues after a failed units.create leaves the run upload to create the unit later, without the metadata the phase meant to set.

import httpx
from tofupilot.v2.errors import TofuPilotError

try:
    dashboard.units.create(serial_number=serial, part_number=part)
except httpx.TransportError:
    phase.retry()          # the server is unreachable: try again later
except TofuPilotError as e:
    if e.status_code >= 500 or e.status_code == 429:
        phase.retry()
    raise                  # a 4xx is a bug in the procedure, not an outage

Run uploads are unaffected: the station queues them and retries on its own.

Requirements

  • CLI 1.9 or later. An older CLI refuses a procedure with a dashboard: block at load.
  • The tofupilot package in the procedure's dependencies (pyproject.toml). A phase or plug asking for dashboard without it fails with that message.

How is this guide?

On this page