| render_with_liquid | false |
|---|
The package system lets you bundle notebooks, data files, Python modules, Prolog knowledge bases, and pre-compiled WASM libraries into a single .zip archive that any SciREPL user can install with one click.
From the catalog: Menu > Browse Packages, Bundles & Workbooks > Install
From a file: Menu > Import Package > select a .zip file
- Create a folder with your files:
my-package/
scirepl.json
demo.ipynb
data/dataset.csv
- Write
scirepl.json:
{
"format_version": "2.0",
"name": "My Package",
"version": "1.0.0",
"description": "A demo package",
"notebooks": [
{ "file": "demo.ipynb", "name": "Demo Notebook" }
],
"files": [
{ "src": "data/dataset.csv", "dest": "/shared/data/dataset.csv", "target": "shared" }
]
}-
Zip it:
cd my-package && zip -r ../my-package.zip . -
Import in SciREPL via Menu > Import Package.
Your notebook can now read the data from any kernel:
# Python
import sharedfs
csv_text = sharedfs.read_text('/shared/data/dataset.csv')# Bash
cat /shared/data/dataset.csvEvery package archive should contain a scirepl.json at the root (or inside one top-level folder). The manifest controls what gets installed and where.
| Field | Type | Required | Description |
|---|---|---|---|
format_version |
"1.0" or "2.0" |
Yes | Manifest format. Use "2.0" for target routing and binary support. |
name |
string | No | Display name shown during install. |
version |
string | No | Semantic version (informational). |
description |
string | No | Short description. |
notebooks |
array | No | Notebook files to load (see below). |
files |
array | No | Files to mount into the virtual filesystem (see below). |
search_paths |
array | No | Prolog library search paths (see below). |
wasm_modules |
array | No | Pre-compiled WASM libraries (see below). |
{ "file": "demo.ipynb", "name": "Demo", "description": "Optional", "kernel": "python" }| Field | Required | Description |
|---|---|---|
file |
Yes | Path to .ipynb inside the archive. |
name |
No | Display name for the notebook tab. |
description |
No | Tooltip or catalog description. |
kernel |
No | Default kernel: "python", "prolog", or "bash". |
{ "src": "data/file.csv", "dest": "/shared/data/file.csv", "target": "shared" }| Field | Required | Description |
|---|---|---|
src |
Yes | Path inside the archive. Trailing / means a directory (all contents are included recursively). |
dest |
Yes | Destination path in the virtual filesystem. |
target |
No | Where to mount: "shared" (SharedVFS), "prolog" (Prolog VFS), "all" (both). Default: "prolog" for v1.0 compat. |
binary |
No | true to preserve as raw bytes (Uint8Array). Auto-detected for .wasm, .png, .jpg, .gif, .bin, .dat, etc. |
{ "alias": "mylib", "dir": "/shared/lib/prolog/mylib" }Adds a Prolog library search path so use_module(library(mylib/foo)) resolves to your package's files.
{
"name": "linalg",
"file": "wasm/linalg.wasm",
"exports": ["matrix_multiply", "svd"],
"ffi": "json",
"js_wrapper": "wasm/linalg_imports.js"
}| Field | Required | Description |
|---|---|---|
name |
Yes | Module name, accessible as window.wasmModules[name]. |
file |
Yes | Path to .wasm binary in the archive. |
exports |
No | Exported function names (informational). |
ffi |
No | "json" enables the JSON FFI calling convention. |
js_wrapper |
No | JS file that returns an imports object for WebAssembly.instantiate(). |
The SharedVFS is an in-memory filesystem accessible to all kernels (Python, Bash, Prolog). Files placed under /shared/ are visible everywhere.
/shared/
lib/ # Libraries
python/ # Python modules (auto-added to sys.path)
prolog/ # Prolog source files
wasm/ # WASM binaries
bin/ # Executable WASM binaries
data/ # Shared datasets (CSV, JSON, etc.)
config/ # Configuration files
/tmp/ # Temporary files (also shared)
| Kernel | Access Method |
|---|---|
| Bash | Direct filesystem access. cat /shared/data/file.csv just works. |
| JavaScript | Direct API. window.sharedVFS.readFile('/shared/data/file.csv', 'utf8') |
| Python | Via the sharedfs module (see below). |
| Prolog | Files with target: "prolog" are mounted in Prolog's VFS. Shared paths are synced after execution. |
The sharedfs module is automatically available in every Python cell. It provides read/write access to the SharedVFS.
import sharedfs
# Read / Write text
text = sharedfs.read_text('/shared/data/greeting.txt')
sharedfs.write_text('/shared/data/output.txt', 'Hello from Python')
# Read / Write binary
data = sharedfs.read_bytes('/shared/data/image.png')
sharedfs.write_bytes('/shared/data/copy.png', data)
# Check existence
if sharedfs.exists('/shared/data/greeting.txt'):
print('File found!')
# List directory contents
entries = sharedfs.listdir('/shared/data')
# Returns: [{'name': 'file.csv', 'size': 123, 'is_dir': False}, ...]
# Create directory
sharedfs.mkdir('/shared/data/subdir')
# Get file metadata
info = sharedfs.stat('/shared/data/file.csv')
# Returns: {'size': 123, 'is_dir': False, 'modified': 1234567890}
# Remove a file
sharedfs.remove('/shared/data/temp.txt')Write in one kernel, read in another:
# Python writes
import sharedfs
sharedfs.write_text('/shared/data/result.csv', 'x,y\n1,2\n3,4')# Bash reads
cat /shared/data/result.csv
# Output: x,y
# 1,2
# 3,4# Bash writes
echo "Hello from Bash" > /shared/data/message.txt# Python reads
import sharedfs
print(sharedfs.read_text('/shared/data/message.txt'))
# Output: Hello from BashPackages can provide Python modules by placing .py files at /shared/lib/python/. These are automatically importable:
In your package:
{
"files": [
{ "src": "python/mymodule.py", "dest": "/shared/lib/python/mymodule.py", "target": "shared" }
]
}In a notebook cell:
import mymodule
mymodule.my_function()The JavaScript kernel runs code natively in the browser — no download, no WASM runtime. It's the most natural way to interact with WASM modules since window.wasmModules is a plain JS object.
Select JS from the language dropdown, or use the %%javascript cell magic in any notebook:
%%javascript
// Runs in the browser's JS engine
console.log("Hello from JavaScript");
const result = await fetch('https://api.example.com/data');
2 + 2 // Last expression is displayed as the cell resultconsole.log/warn/erroroutput appears as cell stdout- Last expression value auto-displayed (like browser devtools)
- End with
;to suppress output (consistent with Python) - Full
async/awaitsupport - Direct access to
window.sharedVFS,window.wasmModules, DOM, etc.
// JSON FFI (most common)
const result = window.wasmModules.sci_math.call('add', {a: 40, b: 2});
console.log(result); // {result: 42}
// List available functions
const info = window.wasmModules.sci_math.call('list_functions', {});
console.log(info.functions);
// Raw exports (for non-JSON-FFI modules)
const exports = window.wasmModules.my_module.exports;
exports.my_function(42);// JS writes to SharedVFS
window.sharedVFS.writeFile('/shared/data/from_js.txt', 'written by JS', 'javascript');# Bash reads it
cat /shared/data/from_js.txt
# Output: written by JSYou can compile Rust libraries to WASM and distribute them as SciREPL packages, making them callable from JavaScript, Python, and Prolog.
cargo init --lib my-wasm-lib
cd my-wasm-libAdd to Cargo.toml:
[lib]
crate-type = ["cdylib"]
[profile.release]
opt-level = "s"
lto = trueThe JSON FFI convention uses three exported functions:
alloc(len) -> ptr— allocatelenbytes, return pointerdealloc(ptr, len)— free allocated memorycall(func_name_ptr, args_json_ptr) -> result_json_ptr— dispatch a function call
Both input and output strings are null-terminated UTF-8.
Example src/lib.rs:
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
#[no_mangle]
pub extern "C" fn alloc(len: usize) -> *mut u8 {
let mut buf = Vec::with_capacity(len);
let ptr = buf.as_mut_ptr();
std::mem::forget(buf);
ptr
}
#[no_mangle]
pub extern "C" fn dealloc(ptr: *mut u8, len: usize) {
unsafe {
drop(Vec::from_raw_parts(ptr, 0, len));
}
}
#[no_mangle]
pub extern "C" fn call(func_ptr: *const c_char, args_ptr: *const c_char) -> *mut c_char {
let func_name = unsafe { CStr::from_ptr(func_ptr) }.to_str().unwrap_or("");
let args_json = unsafe { CStr::from_ptr(args_ptr) }.to_str().unwrap_or("{}");
let result = match func_name {
"add" => {
// Parse {"a": N, "b": M} and return {"result": N+M}
let v: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default();
let a = v["a"].as_f64().unwrap_or(0.0);
let b = v["b"].as_f64().unwrap_or(0.0);
format!(r#"{{"result":{}}}"#, a + b)
}
_ => format!(r#"{{"error":"unknown function: {}"}}"#, func_name),
};
CString::new(result).unwrap().into_raw()
}Add serde_json to dependencies:
[dependencies]
serde_json = "1"# Install the WASM target if you haven't already
rustup target add wasm32-unknown-unknown
# Build
cargo build --release --target wasm32-unknown-unknown
# The .wasm file is at:
# target/wasm32-unknown-unknown/release/my_wasm_lib.wasmOptionally strip the binary:
wasm-strip target/wasm32-unknown-unknown/release/my_wasm_lib.wasmmy-package/
scirepl.json
wasm/my_lib.wasm
demo.ipynb
{
"format_version": "2.0",
"name": "My WASM Library",
"version": "1.0.0",
"description": "A Rust library compiled to WASM",
"wasm_modules": [
{
"name": "my_lib",
"file": "wasm/my_lib.wasm",
"exports": ["add"],
"ffi": "json"
}
],
"notebooks": [
{ "file": "demo.ipynb", "name": "Demo" }
]
}JavaScript (direct access, no bridge needed):
const result = window.wasmModules.my_lib.call('add', {a: 40, b: 2});
console.log(result); // {result: 42}Python:
result = wasm_call('my_lib', 'add', {'a': 40, 'b': 2})
print(result) # {'result': 42}Prolog:
wasm_call(my_lib, add, '{"a": 40, "b": 2}').
% → {"result": 42}If your WASM module doesn't follow the JSON FFI convention, omit "ffi": "json" from the manifest. The module's raw WebAssembly exports are accessible via window.wasmModules[name].exports.
For modules that need a custom imports object (e.g., WASI-like imports), provide a js_wrapper file that returns the imports:
// my_imports.js — must be a single expression that evaluates to an object
({
env: {
log: function(ptr, len) { /* ... */ }
}
})Menu > Export Package creates a .zip containing:
- All open notebooks as
.ipynbfiles - Prolog VFS files (with
target: "prolog") - SharedVFS files from
/shared/data/,/shared/lib/,/shared/config/,/shared/bin/(withtarget: "shared") - A v2.0
scirepl.jsonmanifest
This means you can build a package entirely within SciREPL:
- Create notebooks, write code, add data files
- Export as package
- Share the
.zip— anyone can install it
Packages with format_version: "1.0" continue to work. Files without a target field default to target: "prolog", preserving the original behavior where all files were mounted to the Prolog VFS.
The catalogue (Menu > Browse Packages, Bundles & Workbooks) lists curated offline runtime packages, dependency-aware workbook bundles, and individually installable workbooks. It can also load integrity-checked official release workbooks from a selected remote source. See Verified catalogue sources for channel, cache, privacy, release-pinning, and platform behavior. Installed package state persists across app launches; workbook state is derived from the notebooks currently present.
To add packages to the catalog, edit www/js/package_catalog.js:
{
id: 'my-package',
name: 'My Package',
description: 'Description here',
version: 'v1.0.0',
url: 'https://github.com/user/repo/releases/download/v1.0.0/package.zip',
size: '~500 KB',
kernels: ['python', 'prolog'],
}A bundle groups existing workbook entries and declares any package dependencies. Dependencies are installed first, and already installed packages or workbooks are skipped:
{
id: 'my-workbook-bundle',
name: 'My Workbook Bundle',
type: 'bundle',
requires: ['my-package'],
items: ['tutorial-one', 'tutorial-two'],
}| File | Purpose |
|---|---|
www/js/package_loader.js |
Core package loading, manifest parsing, target routing, WASM module loading |
www/js/package_catalog.js |
Package/bundle/workbook catalog, dependency installation, and installed-state UI |
www/js/shared_vfs.js |
SharedVFS — in-memory filesystem shared across all kernels |
www/js/sharedfs.py |
Python bridge to SharedVFS (import sharedfs) |
www/js/kernels/python.js |
Python kernel — loads sharedfs, syncs /shared/lib/python/ |
www/js/kernels/prolog.js |
Prolog kernel — SharedVFS sync, wasm_call/3 |
www/js/kernels/javascript.js |
JavaScript kernel — native browser execution, direct WASM access |
www/js/kernels/bash.js |
Bash kernel — brush-wasm (coreutils, findutils, grep) |
www/js/prelude.py |
Python prelude — wasm_call() helper |
www/js/file_io.js |
Import/export UI, v2.0 package export |
test_pkg_v2.mjs |
Playwright test suite — package system v2 (15 tests) |
test_js_kernel.mjs |
Playwright test suite — JavaScript kernel (13 tests) |
test_wasm_ffi.mjs |
Playwright test suite — WASM JSON FFI from JS + Python (11 tests) |