This is a template repo to build Canvas apps in Benchling. It contains the data analysis workflows for the Entity Registration in the ClusterClone (Biofoundry) project.
This repository contains:
- A Flask‐based Benchling Canvas application served by Gunicorn
- An Nginx reverse proxy setup (using a predefined server domain) for production deployments
- A Cloudflare tunnel for secure testing without exposing ports directly
- Two separate Docker Compose files: one for testing and one for deployment:
docker-compose-test.yaml— for local/testing environmentsdocker-compose.yaml— for production/deployment on the VM
- Python code to:
- Register entities
- Fill up plates
- Read and create csv files registered in benchling
- Find and fill result tables in a notebook
If you want to learn more about how to build Canvas Apps in Benchling or what other cool things can be applied, log in the Benchling community and check this resources:
- App Canvas | Building an App Workshop - there are 2 videos of >1h each, good guided tour of all the files and how are they connected, using in the repo app-examples-python.
- App Canvas Deep Dive - 1 video of >1h, perfect to learn more about Canvas you’ve already had a chance to play around with it
- Docker (≥ 20.10) and Docker Compose (embedded or plugin) installed on your machine
- An
.envfile at the project root with all required environment variables (see below) - (For production) A domain pointing to your VM and the proper firewall rules.
-
Generate a new repo from this template
- On GitHub, go to Use this template → Create a new repository
- Name it (e.g.
benchling-canvas-foo) and click Create repository from template
-
Clone your newly created repo
git clone git@github.com:<your-org>/<your-new-repo>.git benchling-canvas cd benchling-canvas
-
Make your local copy of the files named
.env.templateand.client_secret.txt.template, and add their local names into.gitignore(if not already listed) :cp .env.template .env cp .client_secret.txt.template .client_secret.txt
- Create & activate a Python virtual environment
python3 -m venv venv source venv/bin/activate - Install dependencies
pip install --upgrade pip setuptools wheel pip install -r requirements-full.txt
Make sure your local .env file includes:
CLIENT_ID– Benchling OAuth client IDAPP_DEFINITION_ID– Benchling app definition IDCLIENT_SECRET_FILE– Path to the secrets file (e.g./run/secrets/app_client_secret)
- Any other variables your Flask app or Nginx configuration requires
Once you have created the app from manifest in Bencling, you'll be able to copy the client secret in client_secret.txt.
You can find a better explanation on the original Benchling repo on Canvas apps: app-examples-python
Create an app from Benchling's developer consol from a manifest, using this repo's manifest as a template. Change the name of your app in info and features. If you need to access specific folders or entity schemas in your code (maybe through Benchling SDK), you should define them here so you don't have problems of access when running the app. The project needs to be defined (maybe also the registry). The rest of the folders and entities depend on your code.
If you have updated the manifest you can change it in Version history (in the developer consol). You will have to define the different folders and entities schemas before running the app for the first time. In the developer consol, inside of the app there is a button in the top right corner View app in the workspace. You can select the right values for each schema under the tab Configuration
-
Generate a New App from Manifest
- In Benchling’s Developer Console, click Create App and choose From Manifest.
- Use this repository’s
manifest.yamlas your template. - Update the app’s name and id in info, and features fields to match your requirements.
-
Define Required Permissions
- If your code needs to access specific folders or entity schemas (e.g., via the Benchling SDK), add those permissions under Configuration in the manifest.
- Ensure you include any Project or Registry definitions your app will use. Missing these will cause access errors at runtime.
- Leave folders and schemas blank, you will select their values from Benchling (this also allows easier implementation in both testing and deployment environments at the same time).
-
Update the Manifest Version (Optional)
- If you make changes to
manifest.yaml, go to Version History in the Developer Console and upload your updated manifest.
- If you make changes to
-
Configure Schemas Before First Run
- Inside your app’s settings in the Developer Console , click View App in the Workspace (top right corner).
- Under the Configuration tab, select the correct values for each folder and entity schema you defined.
- Save your configuration—this ensures that when you run the app, it has permission to read/write the appropriate Benchling data, and you can have different values selected in different environments.
-
Run docker desktop
-
Grant extra access for testing environment
- You may need to grant extra access in the Benchling test environment. In Benchling, open the target project, go to Project Settings → Collaborators, add your app as a collaborator, and assign the Read access policy.
- This is usually not required in Benchling Deployment environment if the app is installed with the right scopes (the information in the Manifest).
- In any case: Make sure all required entity schemas and folders you are using though the app are declared in the manifest so the app can read what it needs.
-
Run the correct Stack
This is intended for local development, using
docker-compose-test.yaml. Run this code in the terminal:docker compose -f docker-compose-test.yaml --env-file .env up --build
docker compose \ -f docker-compose-test.yaml \ --env-file .env \ up --build
Configuring Your Webhook URL
When testing via Cloudflare, copy the HTTPS URL provided by the tunnel and update the Webhook URL in your Benchling App to use that exact address. Be sure to append
/1/webhooksto the end of the HTTPS URL so that Benchling can properly deliver events. After loading a new URL you should test that it works. You can do that from the same developer consol in Benchling, but in your app's tab called webhook testing. There you should selectv2.canvas.initializedunder Preview and Message and then click send. If it's a success you can start using the appUse this on your VM (or any production‐like environment). It assumes your domain is already pointed at this machine, and that ports 80/443 are open.
docker compose \ -f docker-compose.yaml \ --env-file .env \ up --build -d
-
Webhook testing
- Whenever you change the app URL (e.g., switching between deploy and test Docker Compose), verify that Benchling can reach your webhook:
- In Benchling, open Developer Console and select your app.
- Go to the Webhook testing tab (after changing the URL).
- Choose a message type (e.g., canvas.created or canvas.initialized).
- Click Send test.
- If you get 200 OK, the URL and webhook endpoint are working—now you can run the app in a notebook.
What is Nginx and how do we use it?
Nginx is a high-performance web server that also works as a reverse proxy and TLS (HTTPS) terminator. In the deployment stack in our setup it:
- Exposes one public HTTPS URL on the VM (port 443) and routes requests to the correct Docker app based on the path (e.g., /1/webhooks → App A, /2/webhooks → App B).
- Handles SSL certificates (serves the cert/key) so the apps can stay simple and only speak HTTP inside the Docker network.
- Lets multiple Canvas apps share the same VM and domain safely and cleanly.
Certificates
- Use trusted certs (e.g., Let’s Encrypt) and renew them periodically (e.g., via certbot). If you use Let’s Encrypt + certbot (dockerized or on host), renew into /etc/letsencrypt/... or /opt/cert/..., then reload Nginx:
docker compose exec nginx nginx -s reload- !! Do not commit private keys to Git. Keep cert/key paths mounted read-only into the Nginx container.!!
Important gotchas
- Path and code must match: If Benchling routes /1/webhooks/, nginx and your app (app.py) must listen on that path.
- If you only have one app in the VM it can have its own Nginx, but a top-level Ngix container can be developed to manage and route different webhooks (e.g., /1/webhooks → App A, /2/webhooks → App B).
- Firewall/NSG: Ensure port 443 is open to the world and ports 5000 are not exposed on the host (only inside Docker network).
- Again, Don’t commit secrets: Never commit domain.key or any private key to Git.
├── LICENSE
├── README.md <- The top-level README for developers using this project.
├── TODO.md <- A helpful TODO list for project related tasks.
├── .env <- contains usernames/passwords to access database, etc (file is ignored with .gitignore)
├── .env_template <- template to make your own .env file
├── certificates <- eg. ssl certificate needed for sql access (contents ignored with .gitignore)
├── data
│ ├── external <- Data from third party sources.
│ ├── interim <- Intermediate data that has been transformed.
│ ├── processed <- The final, canonical data sets for modeling.
│ └── raw <- The original, immutable data dump.
│
├── notebooks <- Jupyter notebooks. Naming convention is a number (for ordering),
│ and a short `_` delimited description, e.g.
│ `01_initial_data_exploration,ipynb`.
│
├── reports <- Generated analysis as HTML, PDF, LaTeX etc.
│ ├── figures <- Generated graphics and figures to be used in reporting
│ └── presentations <- Any slidedecks used for project meetings can go here
│
├── environment.yml <- The requirements file for reproducing the analysis environment, e.g.
│ generated with `conda env export --no-builds -n clusterclone > environment.yml`.
├── environment_simple.yml <- A simpler version of the environment with only the packages directly
│ installed. Ideally generated through `conda env export --from-history -f environment_simple.yml`.
│
├── src <- Source code for use in this project.
│ ├── __init__.py <- Makes src a Python module
│ ├── data <- Scripts to download or generate data
│ ├── utilities <- Scripts or modules for miscellaneous functions
│ └── visualization <- Scripts to create exploratory and results oriented visualizations
│
└── Anything else?
Some guidelines should be observed consistently across all that contribute to this project, so that this simplified version of a data science structure is still viable.
-
Make sure your notebook works in the repository.
- If you refer to files in notebooks/scripts, they have to exist in the repository - and not just your own computer. Ideally use the dotenv path system to refer to files in the repository.
- Your scripts will need certain libraries to run. Make sure they are listed in the
environment_simple.ymlfile. Some requirements are tricky to resolve due to version conflicts. A full environment export can be put onenvironment.yml. Make sure to built up on the previously established environment and/or test/update the rest of the notebooks/scripts accordingly.
-
Keep the formatting standard.
- Use the black formatter to format your code.
-
Keep data in the structure presented.
- Raw data should always be traceable to sources.
- Interim data need not be well-tracked.
- Processed data should be easily regeneratable from raw data through scripts or notebooks.
Any data processed/generated outside this repository (and prone to change) should go under data/external. When new data file is added to external, the source should be catalogued here.
TODO: A core environment needs to be established. Alternates are fine but one environment would best if possible. Also the requirements file we have now will probably not be sufficient for some of the code.
environment_simple.yml file lists all packages used/needed in scripts and notebooks. This environment can be created by conda using:
conda env create -f environment_simple.yml
Project based on the cookiecutter data science project template. #cookiecutterdatascience