Skip to content

Getting started

Canopy is the backend for SLAC's control-system display platform. It is a single FastAPI application composed of independent modules — each mounted at /<module> and serving its API under /<module>/v1/.... This guide takes you from a fresh clone to reading live PV data, first against the built-in simulation backend (no EPICS required), then against real EPICS IOCs.

Prerequisites

  • uv — installs Python and all dependencies; nothing else is required to run the core PV modules.
  • Docker (optional) — for the full stack with Postgres and Redis, which the persistent modules (Squirrel snapshots, background jobs, batch conversion) need. See Docker Compose.

Install

git clone https://github.com/slaclab/Canopy
cd Canopy
./scripts/setup_dev.sh

This syncs the dev dependencies and installs the pre-commit hooks. See CONTRIBUTING.md for the development workflow.

Configure

Canopy reads configuration from environment variables, and from a .env file in the working directory (real environment variables override the file). Every setting ships with a working default except the API keys. To start from the annotated template:

cp .env.example .env

then uncomment what you need. Settings are namespaced per module: CANOPY_<MODULE>__<FIELD> (double underscore), e.g. CANOPY_VOLE__MAX_POINTS=500000.

API keys

Every data endpoint requires an API key with the right scope. Keys are registered through a single variable:

CANOPY_API_KEYS="<token>:<scope>[+<scope>][@<module>[+<module>]][,<token>:<scope>...]"
  • Scopes are read and write — e.g. dev:read+write or viewer:read,operator:read+write.
  • Clients send the token in the X-API-Key header. WebSocket endpoints also accept ?api_key=<token> for clients that cannot set headers.
  • If CANOPY_API_KEYS is unset, no keys exist and every gated endpoint returns 401.
  • dev:read+write is fine on a laptop. For deployed instances, see the API keys runbook.

Run the API

CANOPY_API_KEYS="dev:read+write@*" uv run uvicorn canopy.main:app

The default EPICS backend is simulation: any PV name resolves to a synthetic sine wave, so you can exercise the whole API with no IOC, network access, or database.

Once running, you have:

URL What it is
http://localhost:8000/ Service info (name, version)
http://localhost:8000/health Liveness check
http://localhost:8000/docs Swagger UI with a dropdown for Canopy and each module's API
http://localhost:8000/hare/docs Standalone Swagger UI for Hare
ws://localhost:8000/hare/v1/ws/pvs Live PV stream

First requests

Read a PV (any name works in simulation):

curl -H "X-API-Key: dev" http://localhost:8000/hare/v1/pvs/DEMO:TEMP
{
  "pv": "DEMO:TEMP",
  "value": 4.79,
  "valueKind": "scalar",
  "severity": "NO_ALARM",
  "tsNs": 1700000000123456789,
  "connected": true,
  "error": null
}

Fetch its display metadata (units, precision, limits — read once to configure a display):

curl -H "X-API-Key: dev" http://localhost:8000/hare/v1/pvs/DEMO:TEMP/describe

Errors always use the same envelope, with a machine-readable code slug:

{
  "code": "invalid_api_key",
  "detail": "Unknown API key"
}

A missing or unknown key returns 401 invalid_api_key; a valid key without the needed scope returns 403 insufficient_read_access / insufficient_write_access.

Live data in the browser

The frontend-demos/ directory contains self-contained HTML pages that exercise the WebSocket APIs — open them directly in a browser:

  • hare-example.html — subscribe to PVs and watch raw update frames.
  • hare-plot-example.html — live strip chart.
  • fox-ws-example.html / fox-rest-example.html — formula evaluation.

Each page defaults to ws://localhost:8000/...?api_key=dev. See the Hare WebSocket guide for the protocol itself.

Talking to real EPICS

Two things change: start (or point at) an IOC, and switch the backend from simulation to epics.

Using the bundled soft IOC (Channel Access, PVs under TEST:):

# Terminal 1 — CA soft IOC
EPICS_CA_ADDR_LIST=127.0.0.1 EPICS_CA_AUTO_ADDR_LIST=NO \
  uv run --with caproto python scripts/sim_ioc.py
# Terminal 2 — Canopy on the real EPICS backend
CANOPY_API_KEYS="dev:read+write@*" CANOPY_EPICS__BACKEND=epics \
  EPICS_CA_ADDR_LIST=127.0.0.1 EPICS_CA_AUTO_ADDR_LIST=NO \
  uv run uvicorn canopy.main:app
curl -H "X-API-Key: dev" http://localhost:8000/hare/v1/pvs/TEST:SINE

For PV Access there is a second soft IOC, scripts/sim_ioc_pva.py, hosting every value kind (TEST:COUNTER, TEST:SINE, TEST:WAVEFORM, TEST:ENUM, TEST:TABLE, ...). Address a PV over PVA with the pva:// prefix:

curl -H "X-API-Key: dev" "http://localhost:8000/hare/v1/pvs/pva://TEST:COUNTER"

Unprefixed names use Channel Access. To use your own IOCs, set EPICS_CA_ADDR_LIST / EPICS_PVA_ADDR_LIST to your facility's values.

Simulation is the default

If you forget CANOPY_EPICS__BACKEND=epics, Canopy starts fine and serves plausible-looking synthetic sine waves for every PV. If every value looks like a smooth ±5 oscillation, check the backend setting.

Full stack with Docker

Postgres-backed features (Squirrel snapshots, job status, batch conversion) need the complete stack — API, worker, Postgres, and Redis:

docker compose -f docker/docker-compose.yml up -d

See Docker Compose for configuration, running database migrations, and the production variant.

Where next

  • Modules overview — what each module does and where its docs live.
  • Hare — live PV reads, writes, and the WebSocket stream.
  • Fox — formulas over live and historical data.
  • Vole — the time-aligned buffer behind Fox.
  • Deployment — running Canopy for real users.