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
name: Label printer
dashboard:
identity: station| Field | Values | Meaning |
|---|---|---|
identity | station (default), any | station 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
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:.
class SerialRegistry:
def __init__(self, dashboard, mode="api"):
self._client = dashboardA 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.
| Run | Identity |
|---|---|
Station daemon, kiosk, tofupilot run --deployment | The station |
tofupilot run . --upload on a machine logged in as a station | The 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 only | The user, with identity: any. Refused at start with identity: station. |
tofupilot run . without --upload | None |
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.
| Variable | Value |
|---|---|
TOFUPILOT_API_KEY | The run's API key |
TOFUPILOT_URL | The instance base URL, without /api (the value tofupilot login --url takes) |
TOFUPILOT_CA_CERT | Path of the private CA bundle, only on an instance that uses one |
import os
from tofupilot.v2 import TofuPilot
client = TofuPilot(server_url=os.environ["TOFUPILOT_URL"] + "/api") # key read from the environmentmain:
- 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 outageRun 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
tofupilotpackage in the procedure's dependencies (pyproject.toml). A phase or plug asking fordashboardwithout it fails with that message.
How is this guide?