PrismNote
Problem
Most notebook tools force a choice: a lightweight local notebook with no real data-warehouse connectivity, or a heavyweight cloud platform for warehouse access with no simple local-first story — and running untrusted notebook code safely usually means bolting on your own sandboxing.
Solution
A Jupyter-compatible data-science notebook with a Rust backend and a React frontend: real local SQL execution, a real sandboxed code-execution engine, and connectors for cloud data warehouses.
Use cases
- Querying local SQLite/DuckDB files or a Postgres/MySQL server from the same notebook, with real integration-tested execution, not a mocked query path.
- Running untrusted or AI-generated code safely — the Docker sandbox gives no network access by default, enforced memory/CPU/process limits, and a wall-clock timeout, rather than executing code in-process.
- Querying a cloud warehouse (Snowflake, BigQuery, Redshift, Databricks, etc.) from a notebook without switching to a separate BI tool.
- Not yet a good fit for: MongoDB access (not implemented, returns an explicit error rather than faking success); data-quality scoring (the one area still not wired to real execution — see Other integrations); Linux/Windows without building from source (prebuilt binary is macOS Apple Silicon only today).
What this is
- Backend: Rust (Axum). Serves the API, runs SQL against local and remote databases, and launches Docker containers for sandboxed code execution.
- Frontend: React + TypeScript (Vite). Notebook UI, SQL cells, schema explorer, results grid.
- The release binary embeds the built frontend, so
prismnoteis a single executable that serves the whole app.
Install
pip install prismnote
prismnote
pip install installs a thin Python launcher (python/) — on first run it
downloads the matching prebuilt server binary from
GitHub Releases and
execs it. As of this version, a prebuilt binary is published for macOS
(Apple Silicon) only; other platforms need to build from source (see
Building below) until more platform binaries are uploaded.
SQL execution
SQL cells run against real databases — there is no mocked or placeholder query path:
| Backend | Status |
|---|---|
| SQLite | Real, embedded (via sqlx), no server required |
| DuckDB | Real, embedded (bundled DuckDB, compiled from source), no server required |
| PostgreSQL | Real, via sqlx; requires a reachable Postgres server |
| MySQL | Real, via sqlx; requires a reachable MySQL server |
All four are covered by integration tests that run genuine
CREATE TABLE / INSERT / SELECT round trips
(crates/server/src/db/executor.rs). SQLite and DuckDB tests always run.
The Postgres/MySQL tests connect to a real server and skip (rather than
fail) when one isn't reachable — point them at a running server with
PRISMNOTE_TEST_PG_PORT / PRISMNOTE_TEST_MYSQL_PORT.
MongoDB is not implemented; connecting to it returns an explicit error rather than a fake success.
Sandboxed code execution
docker_executor.rs runs untrusted code in a brand-new, disposable Docker
container per execution (docker run --rm):
- No network access by default (
--network=none) - Memory, CPU, and process-count limits enforced per run
- A wall-clock timeout that force-kills and cleans up the container
- Real stdout/stderr/exit-code capture
Requires a working Docker installation. Supported languages: Python, Bash/shell, JavaScript (Node), Ruby.
Cloud warehouse connectors
Real connection + query execution for Snowflake, BigQuery, Redshift, Azure
Synapse, Databricks, Athena, Presto, and Trino (crates/server/src/cloud_warehouse/).
AWS-signed requests (Athena, Redshift) use a real SigV4 implementation.
Other integrations
All real API calls, not placeholders:
| Integration | What's real |
|---|---|
| dbt | Shells out to the real dbt CLI; parses its manifest.json/run_results.json |
| GitHub | Real Contents API for notebook backup/sync |
| Airflow | Real REST API v1 (list/trigger DAGs, run status, tasks). DAG creation writes a file to a configured local DAGs folder, since Airflow's API has no DAG-creation endpoint |
| Kubernetes | Real kubectl apply/get pods/scale |
| RunPod | Real GraphQL API for training-instance lifecycle and serverless endpoint deployment |
Two data-quality-scoring code paths (api::get_quality_score,
lineage::data_quality_score) are the one area still not wired to real
execution — they'd need an assertion-storage and table-to-queryable-data
layer that doesn't exist yet, rather than something fakeable in isolation.
Cloud storage (S3/GCS/Azure Blob/Google Drive) is wired to the API.
crates/server/src/cloud_storage.rs has real, unit-tested HTTP clients —
S3 (SigV4-signed), GCS (service-account JWT OAuth), Azure Blob (Shared Key
HMAC-SHA256), Google Drive (OAuth refresh-token) — with real
upload/download/list/delete methods on each client. The /cloud-storage
HTTP endpoints (api::add_cloud_storage, api::list_cloud_storage,
api::remove_cloud_storage) call this code: add_cloud_storage parses the
provider credentials and registers a real client, list_cloud_storage
returns the configured clients, and remove_cloud_storage drops one. A user
who configures real S3 credentials through this API gets a real client
backed by the same code the unit tests exercise. (What is not yet built is
automatic notebook backup scheduling on top of these clients.)
vs Jupyter
Real Jupyter (via nbclient) is the direct comparison the "Jupyter-compatible"
claim above invites. Tested both against the same real notebook (3 cells:
define a DataFrame + variable, use them in the next cell with no re-import,
then a cell that deliberately divides by zero), driven through PrismNote's
real HTTP API (register → login → create notebook → execute) and through
nbclient against a real ipykernel.
| PrismNote (real API) | Jupyter (nbclient + ipykernel) |
|
|---|---|---|
| Kernel/first-cell time | ~4ms once a kernel is warm (see bug below) | 0.57s kernel startup + ~0.17s first cell |
Cross-cell state (cell 2 uses cell 1's x/df, no re-import) |
Correct: 43, 6 |
Correct: 43, 6 |
Real exception (cell 3, 1/0) |
Correct: real ZeroDivisionError traceback, not swallowed |
Correct: real ZeroDivisionError traceback |
"Integrated intelligence" (/api/ai/explain) |
Real — verified against a real local Ollama model (qwen2.5:7b-instruct), returned a real, coherent, non-canned explanation in 5.3s |
N/A (not a Jupyter feature) |
.ipynb format compatibility |
Real, genuine from_ipynb/to_ipynb conversion exists (crates/server/src/files.rs) |
N/A (native format) |
Two real bugs found while running this benchmark — both now fixed:
- Fixed: the kernel process spawn hardcoded
Command::new("python"), which doesn't exist on this (or many modern) machines — onlypython3does. This meant every cell execution failed with the misleading"Kernel not available. Install ipykernel"error, even withipykernelcorrectly installed underpython3. Fixed with apython3-first resolution helper used consistently across all 3 call sites that previously hardcoded"python"(crates/server/src/kernel.rs). Regression test added. - FIXED (was critical): the Python kernel used to be a single
global process shared by every notebook on the server, not one kernel
per notebook. Reproduced live: defined
x = 42in one notebook, then readxfrom a brand-new, unrelated notebook that never defined it — it printed42. Any two notebooks (including two different logged-in users', given the real JWT auth system) shared one Python namespace — a real data-isolation/security issue, not just a quirk. Fixed by replacing the single sharedKernelManagerwith akernel::KernelRegistrykeyed per-notebook (plus ajob:<id>slot per saved job and a fixed system slot for one-shot warehouse/database queries), spawned lazily on first use. Required changing the public API shape for 3 endpoints that previously had no notebook-id parameter at all (kernel/interrupt,kernel/restart,kernel/variables— now/notebooks/:id/kernel/...) plus updating every frontend caller. Re-verified live against the real running server (not just unit tests): the exact reproduction above (x = 42in notebook A, read from notebook B) now correctly raisesNameErrorin notebook B, while notebook A's own state persists correctly across multiple real calls. New Rust regression test (kernel::tests::different_keys_get_independent_kernels_with_isolated_namespaces) spawns two real Python processes and asserts the same isolation. Full detail inROADMAP_HONEST.md.
Bottom line: the core notebook semantics (cross-cell state, real error handling, and now real per-notebook kernel isolation) genuinely work and match real Jupyter behavior — plus real, working local-LLM-backed AI assistance Jupyter doesn't provide out of the box. Idle kernel eviction (kernels are never terminated once spawned) is a real, separate resource-management improvement, not a correctness gap.
Building
git clone https://github.com/Mullassery/PrismNote.git
cd PrismNote
make build
make build builds the frontend first and embeds it into the release
binary — this is the only build path that produces a binary that actually
serves the UI. Running cargo build --release directly will build a
backend with no frontend assets. The binary is written to
target/release/prismnote.
Requirements: Rust (stable), Node 20.19+ (required by Vite 8), and Docker if you want sandboxed code execution or want to test the container-management endpoints.
Development
# Terminal 1: backend on http://localhost:8000
cargo run
# Terminal 2: frontend dev server on http://localhost:5173
cd frontend && npm install && npm run dev
Tests
cargo test --workspace --release # backend
cd frontend && npm test # frontend (vitest)
Real results as of 2026-09-19: backend 170 passed / 1 flaky failure (a
Docker-sandbox timeout-cleanup test that's timing-sensitive under
concurrent test load); frontend vitest 114/114 passed. npm run lint and
npm run test:e2e currently fail in this repo (435 lint errors; 57/85
Playwright tests) — CI's frontend workflow does not currently fail the
build on either, which is a known gap. See
ROADMAP_HONEST.md for the full, unfiltered list of
bugs and gaps found in the most recent audit, including this one.
Configuration
Everything runs with no environment variables set — Google Sign-In and AI
features (code explain/fix/complete, NL-to-SQL, RunPod fine-tuning) are
just disabled until configured. Copy .env.example to
.env and fill in what you need; see the comments in that file for which
variables are required together (e.g. PRISMNOTE_AI_PROVIDER=claude needs
ANTHROPIC_API_KEY) and which are backend-only vs. frontend (VITE_-prefixed).
Project layout
crates/server/ Rust backend: API, SQL executors, Docker sandbox, cloud warehouse connectors
frontend/ React app (components, hooks, API clients)
python/ PyPI launcher package
docs/ architecture notes and screenshots
Contributing, security, status
- CONTRIBUTING.md — dev setup, real test/lint commands
- SECURITY.md — reporting vulnerabilities, known security-relevant facts
- ROADMAP_HONEST.md — verified list of what's broken, untested, not built, or not wired up
- CODE_OF_CONDUCT.md
- CHANGELOG.md
License
Apache License 2.0. See LICENSE for the full terms.
Metadata
Release files for prismnote 1.12.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 | |
|---|---|---|---|
| prismnote-1.12.0.tar.gz | 38.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| prismnote-1.12.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.9 kB
Release files / prismnote-1.12.0.tar.gz
| Download URL | prismnote-1.12.0.tar.gz |
|---|---|
| Size | 38.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
59a512de3e0fbe50db482ad747002fcfde0487cb927df1ebadd9ee76e0eb2696
|
|
BLAKE2b-256 checksum How to use checksums |
12028aef79db5abfd0748c243bfa8ed6755a24a809a31dec93a13b800cc3c0de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / prismnote-1.12.0-py3-none-any.whl
| Download URL | prismnote-1.12.0-py3-none-any.whl |
|---|---|
| Size | 26.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
48ca0b7dd83b6d32bacdffbd9338bf091e96e545da5c8cc1985b97f3da364fc2
|
|
BLAKE2b-256 checksum How to use checksums |
1bbdec50680fcd52aa78fec85732dffe7c5cc8fe33ee187510780418653e8fb9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|