DocsExtensions
BCILattice Documentation

Extensions

Turn any Python package into palette blocks — its own environment, its own sandboxed process, started only when you ask.

v1.0BCINexus Platform · 2026-05-20[email protected]

What an extension is

Everything in the default palette — MNE, PyTorch, scikit-learn, Hugging Face — runs inside BCILattice's own process, because we bundled it and vetted it together with the app. A extension is how you add a library we did not bundle, without touching that process at all.

  Settings › Extensions                  ~/.bcilattice/extensions/<id>/
   [Install] [Start] [Stop] ──spawns──▶  its own venv, its own server, its own port
                                          your library lives here, not inside BCILattice

  ML Suite palette
   your library ▾   Custom Code (your library)   ← the blocks it produced

Install a spec — a package name, a pin, a Git URL — and BCILattice creates a private Python environment for it, starts a small local server inside that environment, and introspects the library's public functions into palette blocks automatically. Nothing you write; the blocks, their ports and their settings come straight from the library's own type hints and defaults.

Bundled provider vs extension

Only one of these gets a process, a Start button, and a Settings page entry.

KindExamplesEnvironmentStart button
BundledBCILattice, MNE, PyTorch, scikit-learn, Hugging Face, XGBoostthe app's ownno — always on
ExtensionCedalion, and anything on the curated shelf or a spec you typeits own venvyes, in Settings › Extensions

The palette itself does not visually separate them — an extension block sits in the canvas next to a bundled one and wires the same way. The difference only matters in Settings, and only because one of the two has a process to manage.

Cedalion: the free example

Cedalion — the IBS-Lab fNIRS/DOT toolbox — ships as an extension and only an extension: there is no in-process fallback and no bundled copy. Its blocks (Beer-Lambert conversion, motion correction, channel-quality metrics, DQR figures) exist once you install it from Settings › Extensions, and not before. It is free on every plan, because it is the extension mechanism's own demonstration — hiding it behind a paywall would hide the feature it is meant to show off.

Installing one

Where: Settings › Extensions.

1

Pick a spec

Browse the curated shelf, or type any install spec — a package name, a version pin (torch==1.2), a Git URL, a private index. What you type is the label everywhere it matters: on the palette, in the consent step, and in a saved graph's requirements.
2

Review the consent step

It names the exact package, its source, and whether the install needs to run the package's own build script (a source build, for the rare package with no prebuilt wheel — Cedalion is one). The dialog defaults to Cancel.
3

Install

BCILattice creates a private environment, resolves and installs the spec into it, and introspects the library once the install finishes. The palette gains the library's blocks under its own name.
4

Start it when you want to run something

A fresh install is not running yet. Press Start the first time you actually need its blocks — nothing about an extension starts automatically, including on app launch.

Curated shelf vs any spec

Free plans install from the curated shelf below — 24 packages we resolved, tested and licence-checked ourselves. Pro and Enterprise can install any spec: a private index, a GitHub URL, a local path. That boundary is deliberate, not just commercial — the free tier only ever runs code we vetted, so the overwhelming majority of installs never touch a package nobody here has looked at.

Start, stop, restart

Each extension row in Settings shows Start / Stop / Logs / Re-introspect / Uninstall. Stopping a library that has gone into an infinite loop cannot be asked nicely — a stuck call cannot be interrupted — so Stop escalates automatically through progressively firmer signals, and a Force stop option appears while the graceful attempt is still in progress rather than only after it times out. Restart does both steps as one action. An engine survives an app restart in its own right — stopping one that was left running from a previous session works the same way as stopping one from the current session.

One extension = one engine limit

How many engines can run at once depends on your plan and is checked every time you press Start, not just at install — so a plan change takes effect immediately rather than only for the next extension you install. Uninstalling frees the disk space but keeps the install on record — reinstalling is one click, not a re-download and re-review.

The curated shelf

Every entry below was actually resolved and installed during vetting — nothing here is a package that turns out not to install. Licences are read from package metadata, not assumed, and only permissive licences are curated; a GPL package can still be installed as a custom spec on Pro, but it is your call to make, not ours to recommend.

fNIRS

Cedalionsource buildBeer-Lambert conversion, motion correction, channel-quality metrics and DQR figures — the IBS-Lab fNIRS/DOT toolbox.MIT

EEG / MEG

AutorejectAutomated epoch rejection and repair, thresholds fitted by cross-validation.BSD-3
PyPREPThe PREP pipeline: robust referencing, line-noise removal, bad-channel detection.MIT
MNE-ICALabelAutomatic ICA component labelling — brain, muscle, eye, heart, line noise.BSD-3
MEEGkitM/EEG denoising: ZapLine, DSS, star, robust detrending, ASR.BSD-3
MNE-ConnectivitySpectral and effective connectivity: coherence, PLI, wPLI, Granger causality.BSD-3
MNE-FeaturesFeature extraction over multivariate time series, sklearn-shaped API.BSD-3
MNE-BIDSRead and write BIDS datasets for MEG, EEG and iEEG.BSD-3
pyXDFRead XDF files — what a Lab Streaming Layer session records to.BSD-2
pybvWrite the BrainVision format, readable by most EEG software.BSD-3
eeglabioExport to EEGLAB .set files, for collaborators working in MATLAB.BSD-3

BCI decoding

BraindecodeDeep learning for EEG — ShallowFBCSPNet, Deep4Net, EEGNet and the training utilities around them.BSD-3
MOABBMother of All BCI Benchmarks: standard public datasets and reproducible evaluation protocols.BSD-3

Biosignals

NeuroKit2ECG, EDA, EMG, RSP and PPG processing, with peak detection and HRV built in.MIT
AntroPyEntropy and complexity measures: sample, permutation, spectral, Higuchi, Petrosian.BSD-3
TensorpacPhase-amplitude coupling, with surrogate methods for testing significance.BSD-3
YASASleep staging and event detection: spindles, slow waves, REMs.BSD-3
noldsNonlinear dynamics on 1-D series: Lyapunov exponents, DFA, correlation dimension.MIT

Dimensionality reduction

UMAPUniform Manifold Approximation and Projection, with a transform for new data.BSD-3
openTSNEParallel t-SNE that can embed new points into an existing map.BSD-3

Acquisition

BrainFlowOne API for many EEG/EMG/ECG headsets — OpenBCI, Muse, Neurosity, Cyton and others.MIT
pylslLab Streaming Layer: receive and send synchronised real-time streams.MIT

Neuroimaging

NiBabelRead and write neuroimaging formats: NIfTI, GIFTI, CIFTI, MGH, DICOM stacks.MIT
NilearnStatistical learning on neuroimaging volumes: masking, decoding, connectomes, plotting.BSD-3

Already-bundled libraries never appear here twice — mne-nirs and pyriemann, for instance, look like they belong under fNIRS but ship with the app already, so their blocks already live under the MNE palette.

How a library becomes blocks

No one hand-writes an extension's blocks. Installing runs the library's own public functions and classes through an introspector, which reads their type hints, defaults and docstrings and emits the same kind of catalog row the bundled palette is built from — so an extension block gets a real inspector, a tooltip and typed ports the same way any other block does.

Ports vs settings

What ends up wireable versus what ends up a field follows one rule:

In the library's signatureBecomes
An array, a data frame, or one of the library’s own classesan input port
A scalar with a defaulta setting
A scalar with no defaulta required setting
A fixed set of choices (an enum)a dropdown setting
A physical quantity (e.g. a frequency)a unit-suffixed setting, e.g. fmin_hz
Anything the introspector cannot classifyan input port — never silently dropped

The result is a plain, inspectable catalog file — if the heuristic guesses a port wrong for a specific function, that is a text-file fix, not a code change.

Core vs advanced blocks

Introspection is deliberately greedy — it emits a block for every public callable it can classify. Cedalion alone yields 329. Shipping all 329 onto the front page would repeat the mistake the bundled catalog already made, so every generated block starts at advanced and stays out of the way.

An extension may then carry a small hand-written curated.yaml that promotes a handful of targets to core — giving each one a display name, a palette category, defaults and unit hints. Cedalion promotes 18, so a first-run fNIRS palette reads Cedalion SNIRF Reader, Cedalion Beer-Lambert, Cedalion Frequency Filter rather than cedalion.io.read_snirf and 328 siblings.

TierWhere it showsNamed by
coreFront of the palette category, on first runThe curated manifest, by hand
advancedBehind the Advanced toggle, searchableThe library’s own dotted path

An extension with no curation file is still perfectly usable — all of its blocks simply live at advanced. The file is plain YAML, so you can write your own for a library you installed and give it the front page you want.

Custom Code, per extension

Every installed extension also gets its own Custom Code (your library) block — a Pro feature. Your snippet runs inside that extension's own environment, with the library already imported and ready to use, so it can reach functionality the generated blocks do not cover without leaving the sandbox the rest of the extension runs in.

What is actually isolated

An extension runs code we did not write and did not review, so it is treated as a genuinely separate trust boundary, not just a separate folder:

  • It runs as its own operating-system process, in its own Python environment, confined so it cannot spawn child processes and cannot exceed set memory and CPU limits.
  • It receives no BCILattice or BCINexus credential — no login token, no licence key, no route back into the app's own local server.
  • Only the library's own public functions are callable — a graph cannot reach an arbitrary Python builtin, because it was never introspected in the first place, not because it is filtered after the fact.
  • General internet access is allowed by default (many libraries need it — an atlas download, a model checkout) and can be turned off per extension; a route back to BCINexus itself is refused unconditionally.

Stated plainly, not glossed over

Filesystem confinement inside an extension's environment is enforced by convention today — its working directory, temp directory and cache all point inside its own folder — but a library that deliberately writes to an absolute path elsewhere is not stopped by the OS. This is a known, documented gap, not an oversight, and it narrows as the sandbox work continues.

Objects never leave the engine

A downstream block that consumes an extension's output does not receive the real object — it receives a small reference token. The extension's own server keeps the actual data and dereferences the token when the next block needs it. Plain numbers and small arrays cross normally; a large array crosses through a temporary file rather than being encoded into a network request. This is what lets two different, even conflicting, versions of the same library run at once without either one touching the other's data.

Free vs Pro vs Enterprise

CapabilityFreeProEnterprise
Curated shelf (24 extensions), including Cedalion
Start / stop an engine
Simultaneous engineslimitedhighunlimited
Any spec — Git URL, local path, private index
Custom Code inside an extension
Org-wide allowlists and install audit

Downgrading never deletes an environment already on disk — anything already installed keeps working, only a further install is refused.

Sharing a graph that needs one

A pipeline or workflow that uses extension blocks records which extensions it needs, derived automatically from the blocks actually on the canvas. Opening someone else's graph, or importing one from Community, never installs anything on its own — you are shown what it asks for and choose whether to install and start each one yourself.

A requirement is a request, never an instruction

Nothing about loading a shared graph can trigger an install by itself. If a graph's declared requirement carries anything unusual — flags, a raw shell string — it is shown as plain text with a warning and no install button, so a person stays the one deciding whether to type it in.

Publishing a study to BCINexus carries the same list, and it is shown on the study page before download — so a collaborator knows what they will need to install before they commit to downloading anything.

Troubleshooting

MessageWhat to do
"…not running — start it in Settings › Extensions"The engine was never started, or was stopped. Start it, then re-run.
Install fails partway throughThe failing command is shown verbatim in the log — usually a dependency with no prebuilt wheel. Check whether the entry needs a source build and re-try if it timed out.
Engine will not respond to StopUse Force stop, offered next to the normal Stop button while the graceful attempt is still running.
A block from a colleague’s graph is greyed outThat extension is not installed here yet. Install it from the requirement shown on import, or from the curated shelf if it is on it.
Version shown differs from what the graph was built onReported, not blocked — extensions upgrade independently of the graphs that use them. Re-check results if the new version changed defaults.

See also Using the ML Suite for where extension blocks fit into a pipeline, and Advanced Blocks for the bundled DL Training family they are commonly wired alongside.

Extensions v1.0 · BCINexus Platform · 2026-05-20