NeXus NeuroMirror
A clean starter for a four-channel EEG neurofeedback prototype built on the original Mind Media NeXus-10 amplifier with BioTrace+ acquisition, plus an offline-first AI adaptation loop. The repository ships a montage, a phased workflow, and a diagnostic verifier that turns a raw EDF/EDF+ export into a machine-readable report and a small set of clear figures.
Safety & research disclaimer. This is a research / educational prototype, not a medical device. It is not intended to diagnose, treat, cure, or prevent any disease, and it makes no clinical claims. Use only on yourself or consenting research participants under an appropriate protocol, and always follow the manufacturer's documentation for hardware operation, electrode application, reference, and ground placement. Neurofeedback can cause fatigue, headache, or discomfort; stop if that occurs.
Why offline-first
No public, confirmed real-time SDK exists for this legacy NeXus-10 setup.
The design is therefore offline-first with blockwise adaptation: you record
a block in BioTrace+, export to EDF/EDF+, analyze/adapt between blocks, and feed
updated parameters into the next block. A clean extension path for a future
real-time SDK is described in docs/architecture.md.
Initial four-channel montage
| Site | Role | Rationale |
|---|---|---|
| Fz | EEG | Midline frontal — attention, frontal theta, executive control |
| FCz | EEG | Fronto-central — sensorimotor / error-related activity |
| Pz | EEG | Midline parietal — P300, alpha, attention allocation |
| Oz | EEG | Midline occipital — visual alpha (eyes open/closed contrast) |
- Reference: linked ears / mastoids (A1+A2, equivalently M1+M2), kept identical across every session.
- Ground: manufacturer-approved placement only — follow the Mind Media NeXus-10 / BioTrace+ manual for your hardware revision. Do not improvise.
- Impedance / signal quality: follow the BioTrace+ / Mind Media signal-quality guidance for your hardware and electrode type, and balance impedances across channels. (No official NeXus-10 impedance limit is cited here, so no fixed threshold is asserted.)
Full definition: configs/montage.yaml.
Acquisition-to-model pipeline
flowchart LR
A["NeXus-10<br/>amplifier"] --> B["BioTrace+<br/>acquisition"]
B --> C["Export<br/>EDF / EDF+"]
C --> D["verify CLI<br/>(MNE load)"]
D --> E["Diagnostics<br/>channels · sfreq · RMS · p2p · markers"]
D --> F["Resample to 200 Hz<br/>filter · epoch"]
F --> G["Features<br/>bandpower per band"]
G --> H["Model<br/>baseline classifier"]
E --> R["Report<br/>JSON + PNG/SVG"]
H --> P["Block parameters<br/>for next session"]
Blockwise closed-loop workflow
flowchart TD
S["Session start<br/>montage + impedance check"] --> B1["Record block<br/>in BioTrace+"]
B1 --> X["Export EDF/EDF+<br/>with event markers"]
X --> V["verify + diagnose"]
V -->|hard failure| Fix["Fix setup<br/>re-record block"]
Fix --> B1
V -->|ok| An["Analyze block<br/>features + metrics"]
An --> Ad["Update model /<br/>feedback parameters"]
Ad --> B2["Next block<br/>with new parameters"]
B2 --> X
Ad --> Rep["Session report<br/>+ provenance"]
Quick start
# 1. Install (Python >= 3.10)
python -m venv .venv && source .venv/bin/activate
make install-dev # or: pip install -e ".[dev]"
# 2. Generate a synthetic EDF and run the verifier end-to-end
make demo
# 3. Verify a real BioTrace+ export
nexus-neuromirror verify path/to/session.edf \
--config configs/project.example.yaml \
--out reports/diagnostic
The verifier:
- loads EDF/EDF+ through MNE,
- reports channel names, sample rate, duration, unit assumptions, and per-channel RMS / peak-to-peak,
- detects EDF+ annotations and likely marker channels,
- validates the four expected EEG channels using configurable aliases,
- writes a machine-readable
diagnostic.jsonplus PNG/SVG figures (multichannel trace, PSD, marker timeline), - exits nonzero on a hard validation failure (missing channel, too short, disallowed sample rate, missing required events).
Web dashboard (web/)
A local, single-user web dashboard wraps the same verifier so you can upload BioTrace+ exports through a browser, view diagnostics, and (optionally) commit raw recordings plus generated reports to this private repository. It is a private prototype — there are no user accounts; access is controlled entirely by the authenticated preview/hosting boundary you run it behind.
Not a medical or diagnostic tool. The dashboard surfaces the same research diagnostics as the CLI and makes no clinical claims.
Architecture
- Backend — FastAPI (
web/backend/nnm_web/) that reuses thenexus_neuromirrorpackage for EDF analysis. Endpoints: health/status, session catalog, session detail, secure multipart upload, artifact serving, and repo-sync status. - Frontend — React + Vite + TypeScript + Tailwind (
web/frontend/), hash-routed, with Overview, Upload, Sessions, and Session-detail pages, light/dark themes, and full skeleton/empty/error/unsupported-format states.
Accepted upload formats
| Format | Extensions | Handling |
|---|---|---|
| EDF / EDF+ | .edf |
Analyzed with the MNE verifier → report + figures |
| ASCII / CSV | .csv, .txt, .asc |
Cataloged only (checksum + metadata) in the MVP |
| MATLAB | .mat |
Cataloged only (checksum + metadata) in the MVP |
| BCD | .bcd |
Archival only — never parsed |
Privacy & security model
- EEG/neurofeedback data is sensitive. Uploads are stored in this repository, analyzed (EDF/EDF+), and committed locally. They are pushed to the private GitHub remote only when server-side GitHub credentials are configured and the remote is reachable; otherwise the local save, analysis, and local commit still complete and the push is reported as pending/failed without data loss. Sharing the running site grants upload access — keep it behind an authenticated boundary.
- Every upload is sanitized (path-traversal rejected, filename normalized), extension-restricted, size-limited, and SHA-256 checksummed. Raw file contents are never logged.
- The
.gitignorekeeps all real recordings and generated reports out of the repo. Accepted uploads are added only through an explicit, per-file safe force-add in the upload path — the data directories are never broadly un-ignored. The only intentionally committed report is the synthetic demo underreports/diagnostic_demo/, which powers the Overview page.
8 MB upload limit
The hosted preview proxy rejects requests over 10 MB, so the dashboard caps
uploads at 8 MB (NNM_MAX_UPLOAD_BYTES). A 60-second four-channel synthetic
EDF is ~150 KB, well within range. For larger real recordings, use the CLI
directly.
GitHub sync & runtime credentials
Repo sync uses server-side git/gh only — GitHub credentials are never
exposed to the frontend. Automatic push is not guaranteed: the UI only
promises a push when the /api/repo-sync credential check succeeds. If
credentials are missing or a push fails, the upload is still saved, analyzed, and
committed locally, and the pending/failed push is surfaced in the UI without data
loss — it can be completed later with an authorized git push (no re-upload
needed).
A known constraint: a long-lived preview server process does not inherit the
foreground GitHub CLI credential, so in-process pushes from a hosted preview
typically fail even though the local commit succeeds. When that happens, complete
the push with the authorized foreground GitHub CLI and, if desired, reconcile the
session's metadata.json git block (pushed, commit_url, error) to match.
When running behind a hosted preview, inject git credentials into the server
environment at runtime for automatic push; they may not persist across a hosted
session, in which case sync degrades gracefully to local-only.
Run it locally
# Backend (from repo root, with the project venv active)
pip install -r web/backend/requirements.txt
cd web/backend && PYTHONPATH=. uvicorn nnm_web.app:app --port 8000
# Frontend (separate terminal)
cd web/frontend && npm install && npm run dev # dev server proxies /api -> :8000
For a single-process deployment, build the frontend (npm run build) — the
backend serves web/frontend/dist/ at / automatically when present. The build
is packaged for nested-URL previews too: vite.config.ts uses base: './' for
relative asset paths, and src/api.ts uses the __PORT_8000__ proxy placeholder
for its API base. Deploy the web/frontend/dist directory and keep the backend
running on port 8000. See web/README.md → Deployment
packaging.
Useful environment variables: NNM_GIT_SYNC_ENABLED (default on),
NNM_GIT_REMOTE, NNM_GIT_BRANCH, NNM_MAX_UPLOAD_BYTES, NNM_UPLOADS_SUBDIR,
NNM_REPORTS_SUBDIR, NNM_CONFIG_PATH, NNM_REPO_ROOT.
Tests: cd web/backend && python -m pytest (backend);
cd web/frontend && npm run typecheck && npm run lint && npm run build
(frontend). See web/README.md for details.
Research & experiments
Three design/research deliverables accompany the runtime code. They are documentation and a standalone experiment — none of them change the verifier, the web dashboard, or the runtime data path, and none of them add a dependency to the CLI or the web backend.
Reports (docs/research/)
docs/research/neuromirror-research-thesis.md— a falsifiable research program for multimodal-supervised cognitive-state modeling and sham-controlled closed-loop testing on the four-channel montage. It defines the science (hypotheses, operational constructs, controls, stopping rules), explicitly fences off consciousness/thought-decoding claims, and treats the four-channel montage as a hard constraint on spatial inference.docs/research/neuralset-integration-spec.md— an implementation-ready spec for making BioTrace+ EDF/EDF+ sessions consumable by NeuralSet (Meta FAIR's Neuro-AI data-engineering framework). It maps NeuroMirror concepts onto NeuralSet's Events/Extractors/Segmenter/Batch abstractions as a proposed, separate adapter layer — it does not modifynexus_neuromirrorand keeps NeuralSet/PyTorch out of the web backend's request path.
Both reports preserve their inline citations and Sources/References sections.
Experiment scaffold (experiments/neuralset-experiment/)
A standalone, executable first-experiment scaffold
(experiments/neuralset-experiment/)
that runs a complete, conservative baseline on tidy CSV/TSV EEG: validate →
normalize markers → conservative preprocess (artifacts flagged, never silently
deleted) → event-aligned windowing → Welch bandpower features → a baseline
classifier with session-aware cross-validation (leave-one-session-out /
GroupKFold, so windows never leak across train/test) → metrics.json + plots. A
demo command generates synthetic data so the whole pipeline runs with no
private files. Committed synthetic reference outputs live in
experiments/neuralset-experiment/outputs/demo/
(the large raw demo CSV is regenerated, not committed).
cd experiments/neuralset-experiment
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
neuralset-scaffold demo --out outputs/demo # end-to-end, synthetic
pytest && ruff check src tests # 54 tests, lint
How these fit together (experiment ↔ web UI ↔ NeuralSet adapter boundary)
The pieces are deliberately layered so each can evolve without breaking the others:
- Web UI / CLI verifier (shipped, runtime). The FastAPI/React dashboard and
the
nexus-neuromirror verifyCLI are the production path: they ingest a BioTrace+ EDF/EDF+ export and emit diagnostics only — channel/sample-rate validation, RMS/peak-to-peak, marker detection, and reproducible JSON+figure reports. They make no modeling, medical, or mental-state claims and never import the experiment or NeuralSet. - Experiment scaffold (research, offline). Picks up downstream of that
verified boundary. It is where windowing, feature extraction, and baseline
modeling actually live — the parts the main pipeline configures
(
model.window_s, bands) but intentionally does not implement. It runs out-of-band on exported/synthetic data, is installed as its own package with its own venv, and never touches the web request path. - NeuralSet adapter boundary (optional seam). Both the spec and the
scaffold expose NeuralSet as an optional, gracefully-degrading seam, never a
requirement. In the scaffold,
neuralset_adapter.pyis an explicit wiring point: if theneuralsetpackage is importable it can provide features, otherwise the scaffold falls back to its built-in spectral features and reportsavailable=false. No NeuralSet API is fabricated. This keeps the heavy Neuro-AI dependency isolated to the experiment layer and out of the verifier and the web backend — exactly the boundary the integration spec prescribes.
In short: the web UI verifies and reports; the experiment models; the NeuralSet adapter is an optional research seam — and the two never share a dependency graph.
Success criteria (prototype)
- A 60-second bring-up recording exports to EDF/EDF+ with all four channels.
nexus-neuromirror verifypasses with no hard failures.- Per-channel RMS falls in a plausible band (~0.5–150 µV) with no dead leads.
- Event markers round-trip from BioTrace+ into annotations or a marker channel.
- Occipital Oz shows a visible eyes-closed alpha increase in the PSD.
- Reports are reproducible and contain no personally identifying information.
Repository layout
configs/ montage.yaml, project.example.yaml
docs/ setup-checklist.md, architecture.md
docs/research/ research thesis + NeuralSet integration spec (documentation)
experiments/ neuralset-experiment/ — standalone executable baseline scaffold
src/ nexus_neuromirror/ (config, edf, metrics, markers, verify, viz, report, cli)
tests/ synthetic-EDF based tests (no private data required)
web/ FastAPI backend + React/Vite dashboard — see web/README.md
web/vercel/ — GitHub-backed serverless deployment
data/ (git-ignored) recordings — see data/README.md
reports/ (git-ignored) generated diagnostics — see reports/README.md
(exception: synthetic reports/diagnostic_demo/ is committed)
Documentation
docs/setup-checklist.md— hardware/software inventory, NeXus-10 bring-up, BioTrace+ configuration, 60 s diagnostic, EDF/EDF+ export, marker verification, data-governance checklist.docs/architecture.md— clean-architecture narrative with component, sequence, and data-flow diagrams; offline/blockwise rationale; real-time SDK extension path.data/README.mdandreports/README.md— why neural data and generated reports must not be committed.docs/research/neuromirror-research-thesis.mdanddocs/research/neuralset-integration-spec.md— the research thesis and the NeuralSet integration spec (see Research & experiments above).experiments/neuralset-experiment/README.md— the standalone baseline experiment scaffold.
External resources & datasets
These support development, benchmarking, and model prototyping. NeXus-10 links are for manuals and export documentation only — no real-time SDK is implied.
- Mind Media — BioTrace+ software & NeXus manuals / data export: https://www.mindmedia.com/en/support/downloads/ and https://www.mindmedia.com/en/products/biotrace-software/
- Brain Invaders adaptive vs. non-adaptive P300 dataset (Zenodo): https://zenodo.org/records/2669187
- PhysioNet EEG Motor Movement/Imagery Dataset (EEGMMIDB): https://physionet.org/content/eegmmidb/1.0.0/
- CBraMod (EEG foundation model, code): https://github.com/wjq-learning/CBraMod
- REVE (EEG model on Hugging Face): https://huggingface.co/brain-bzh/reve-base
- MNE-Python (EDF/EDF+ IO and analysis): https://mne.tools/stable/index.html
- EDF / EDF+ format specification: https://www.edfplus.info/specs/index.html
License
MIT — see LICENSE.
Metadata
Release files for nexus-neuromirror 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nexus_neuromirror-0.2.0.tar.gz | 33.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nexus_neuromirror-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.8 kB
Release files / nexus_neuromirror-0.2.0.tar.gz
| Download URL | nexus_neuromirror-0.2.0.tar.gz |
|---|---|
| Size | 33.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
518f4956fc373a902a88f808f5d5d25baa79e1e9dd188db63b4fa0c2570731a9
|
|
BLAKE2b-256 checksum How to use checksums |
2391b596d509edc8d1e376f1bdb59eac3cec5b7432da402d341ef9a61c3ee036
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency logRelease files / nexus_neuromirror-0.2.0-py3-none-any.whl
| Download URL | nexus_neuromirror-0.2.0-py3-none-any.whl |
|---|---|
| Size | 26.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
de98b7efb5c159e7a9cefb8a02765940177a62bb4d473b5b208930c0ca10cfd2
|
|
BLAKE2b-256 checksum How to use checksums |
b21028cda85aeb4c6f05322aaa81960a79a161f03a5ee0474c068ce882e79bda
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency log