This project has been archived by its maintainers, and is no longer receiving any updates.
🚀 Overview
Marimushka is a powerful tool for exporting marimo notebooks to HTML/WebAssembly format with custom styling. It helps you create beautiful, interactive web versions of your marimo notebooks and applications that can be shared with others or deployed to static hosting services like GitHub Pages.
Marimushka "exports" your marimo notebooks in a stylish, customizable HTML template, making them accessible to anyone with a web browser - no Python installation required!
✨ Features
- 📊 Export marimo notebooks (.py files) to HTML/WebAssembly format
- 🎨 Customize the output using Jinja2 templates
- 📱 Support for both interactive notebooks and standalone applications
- Notebooks are exported in "edit" mode, allowing code modification
- Apps are exported in "run" mode with hidden code for a clean interface
- 🌐 Generate an index page that lists all your notebooks and apps
- 🔄 Integrate with GitHub Actions for automated deployment
- 🔍 Recursive directory scanning to find all notebooks in a project
- 🧩 Flexible configuration with command-line options, Python API, and config files
- 🔒 Security-first design with multiple protection layers
- Path traversal protection
- TOCTOU race condition prevention
- DoS protections (file size limits, timeouts, worker bounds)
- Error message sanitization
- Audit logging for security events
- Secure file permissions
📋 Requirements
- Python 3.11+
- marimo (installed automatically as a dependency)
- uvx (recommended to bypass installation)
📥 Installation
We do not recommend installing the tool locally. Please use
# install marimushka on the fly
uvx marimushka
# or
uvx marimushka --help
🛠️ Usage
Command Line
# Basic usage (some help is displayed)
uvx marimushka
# Start exporting, get some help first
uvx marimushka export --help
# Do it
uvx marimushka export
# Specify a custom template
uvx marimushka export --template path/to/template.html.j2
# Specify a custom output directory
uvx marimushka export --output my_site
# Specify custom notebook and app directories
uvx marimushka export --notebooks path/to/notebooks --apps path/to/apps
# Disable sandbox mode (use project environment)
uvx marimushka export --no-sandbox
Configuration File
Marimushka supports configuration via a .marimushka.toml file in your project root:
[marimushka]
output = "_site"
notebooks = "notebooks"
apps = "apps"
sandbox = true
parallel = true
max_workers = 4
timeout = 300
[marimushka.security]
audit_enabled = true
audit_log = ".marimushka-audit.log"
max_file_size_mb = 10
file_permissions = "0o644"
See .marimushka.toml.example in the repository for a complete example with documentation.
Project Structure
Marimushka recommends your project to have the following structure to be aligned with its default arguments. However, it is possible to inject alternative locations
your-project/
├── notebooks/ # Static marimo notebooks (.py files)
├── notebooks_wasm/ # Interactive marimo notebooks (.py files)
├── apps/ # Marimo applications (.py files)
└── custom-templates/ # Optional: Custom templates for export
└── custom.html.j2 # Your custom template
Marimo Notebook Requirements
By default, marimushka exports notebooks using the --sandbox flag.
This ensures that the export process runs in an isolated environment, which is safer and ensures that your notebook's dependencies are correctly defined in the notebook itself (e.g. using /// script metadata).
When developing or testing notebooks locally, it is good practice to use the --sandbox flag:
# Running a notebook with the sandbox flag
marimo run your_notebook.py --sandbox
# Or with uvx
uvx marimo run your_notebook.py --sandbox
If you need to export notebooks that rely on the local environment (e.g. packages installed in the current venv but not declared in the notebook), you can disable the sandbox:
uvx marimushka export --no-sandbox
GitHub Action
You can use marimushka in your GitHub Actions workflow to automatically export and deploy your notebooks:
permissions:
contents: read
jobs:
export:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Export marimo notebooks
uses: jebel-quant/marimushka@v0.2.1
with:
template: 'path/to/template.html.j2' # Optional: custom template
notebooks: 'notebooks' # Optional: notebooks directory
apps: 'apps' # Optional: apps directory
notebooks_wasm: 'notebooks' # Optional: interactive notebooks directory
The action will create a GitHub artifact named 'marimushka' containing all exported files. The artifact is available in all jobs further declaring a dependency on the 'export' job.
Action Inputs
| Input | Description | Required | Default |
|---|---|---|---|
notebooks |
Directory containing marimo notebook files (.py) to be exported as static HTML notebooks. | No | notebooks |
apps |
Directory containing marimo app files (.py) to be exported as WebAssembly applications with hidden code (run mode). | No | apps |
notebooks_wasm |
Directory containing marimo notebook files (.py) to be exported as interactive WebAssembly notebooks with editable code (edit mode). | No | notebooks |
template |
Path to a custom Jinja2 template file (.html.j2) for the index page. If not provided, the default Tailwind CSS template will be used. | No |
Example: Export and Deploy to GitHub Pages
name: Export and Deploy
on:
push:
branches: [ main ]
jobs:
export-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Export marimo notebooks
uses: jebel-quant/marimushka@v0.2.1
with:
notebooks: 'notebooks'
apps: 'apps'
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: artifacts/marimushka
branch: gh-pages
Advanced CI/CD Patterns
GitLab CI Integration
Marimushka works seamlessly with GitLab CI/CD:
# .gitlab-ci.yml
stages:
- export
- deploy
export-notebooks:
stage: export
image: python:3.11
script:
- pip install uv
- uvx marimushka export --output public
artifacts:
paths:
- public
only:
- main
pages:
stage: deploy
dependencies:
- export-notebooks
script:
- echo "Deploying to GitLab Pages"
artifacts:
paths:
- public
only:
- main
CircleCI Integration
# .circleci/config.yml
version: 2.1
jobs:
export:
docker:
- image: cimg/python:3.11
steps:
- checkout
- run:
name: Install dependencies
command: pip install uv
- run:
name: Export notebooks
command: uvx marimushka export
- persist_to_workspace:
root: .
paths:
- _site
- store_artifacts:
path: _site
destination: notebooks
workflows:
main:
jobs:
- export
Netlify Integration
Deploy directly to Netlify from GitHub Actions:
# .github/workflows/netlify.yml
name: Deploy to Netlify
on:
push:
branches: [main]
pull_request:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Export notebooks
uses: jebel-quant/marimushka@v0.2.1
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v2
with:
publish-dir: artifacts/marimushka
production-branch: main
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Deploy from GitHub Actions"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
Vercel Integration
Deploy to Vercel using GitHub Actions:
# .github/workflows/vercel.yml
name: Deploy to Vercel
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Export notebooks
uses: jebel-quant/marimushka@v0.2.1
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
working-directory: artifacts/marimushka
AWS S3 + CloudFront
Deploy to AWS infrastructure:
# .github/workflows/aws.yml
name: Deploy to AWS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Export notebooks
uses: jebel-quant/marimushka@v0.2.1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Sync to S3
run: |
aws s3 sync artifacts/marimushka/ s3://${{ secrets.S3_BUCKET }}/notebooks/ \
--delete \
--cache-control "public, max-age=3600"
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DIST_ID }} \
--paths "/*"
For more CI/CD recipes and patterns, see:
- RECIPES.md - Comprehensive recipes and examples
- FAQ.md - Common deployment questions
- TROUBLESHOOTING.md - CI/CD troubleshooting
🎨 Customizing Templates
Marimushka uses Jinja2 templates to generate the 'index.html' file. You can customize the appearance of the index page by creating your own template.
The template has access to two variables:
notebooks: A list of Notebook objects representing regular notebooksapps: A list of Notebook objects representing app notebooksnotebooks_wasm: A list of Notebook objects representing interactive notebooks
Each Notebook object has the following properties:
display_name: The display name of the notebook (derived from the filename)html_path: The path to the exported HTML filepath: The original path to the notebook filekind: The type of the notebook (notebook / apps / notebook_wasm )
Example template structure:
<!DOCTYPE html>
<html>
<head>
<title>My Marimo Notebooks</title>
<style>
/* Your custom CSS here */
</style>
</head>
<body>
<h1>My Notebooks</h1>
{% if notebooks %}
<h2>Interactive Notebooks</h2>
<ul>
{% for notebook in notebooks %}
<li>
<a href="{{ notebook.html_path }}">{{ notebook.display_name }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% if apps %}
<h2>Applications</h2>
<ul>
{% for app in apps %}
<li>
<a href="{{ app.html_path }}">{{ app.display_name }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</body>
</html>
🔒 Security
Marimushka is designed with security as a priority. See SECURITY.md for details on:
- Security features and protections
- Best practices for secure deployment
- Configuration options for enhanced security
- Audit logging
- Vulnerability reporting
👥 Contributing
Contributions are welcome! Here's how you can contribute:
- 🍴 Fork the repository
- 🌿 Create your feature branch (
git checkout -b feature/amazing-feature) - 💾 Commit your changes (
git commit -m 'Add some amazing feature') - 🚢 Push to the branch (
git push origin feature/amazing-feature) - 🔍 Open a Pull Request
Development Setup
# Clone the repository
git clone https://github.com/jebel-quant/marimushka.git
cd marimushka
# Install dependencies
make install
# Run tests
make test
# Run linting and formatting
make fmt
📚 Documentation
Marimushka has comprehensive documentation to help you get the most out of it:
Core Documentation
- README.md - This file. Getting started guide and feature overview
- CHANGELOG.md - Detailed version history with migration notes
- MIGRATION.md - Version upgrade guides with code examples
- API.md - Complete Python API reference for programmatic usage
User Guides
-
TROUBLESHOOTING.md - Common issues and solutions
- Installation problems
- Export failures
- Template errors
- Performance issues
- GitHub Action troubleshooting
-
RECIPES.md - Real-world usage patterns and examples
- Basic workflows
- CI/CD integration (GitHub, GitLab, CircleCI)
- Custom templates
- Advanced patterns
- Deployment strategies
-
FAQ.md - Frequently asked questions
- Quick answers to 50+ common questions
- Organized by topic
- Search-friendly format
Configuration
- .marimushka.toml.example - Configuration file example
- src/marimushka/templates/README.md - Template customization guide
Security & Contributing
- SECURITY.md - Security features, best practices, and reporting
- CONTRIBUTING.md - How to contribute to the project
- CODE_OF_CONDUCT.md - Community guidelines
Quick Links
| I want to... | See... |
|---|---|
| Get started quickly | README.md - Installation |
| Fix an error | TROUBLESHOOTING.md |
| See real examples | RECIPES.md |
| Find a quick answer | FAQ.md |
| Upgrade versions | MIGRATION.md |
| Use the Python API | API.md |
| Deploy to GitHub Pages | README.md - GitHub Action |
| Customize templates | src/marimushka/templates/README.md |
| Report a security issue | SECURITY.md |
| Contribute | CONTRIBUTING.md |
📄 License
This project is licensed under the MIT License.
🙏 Acknowledgements
Metadata
Release files for marimushka 0.4.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 | |
|---|---|---|---|
| marimushka-0.4.0.tar.gz | 42.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| marimushka-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 84.1 kB
Release files / marimushka-0.4.0.tar.gz
| Download URL | marimushka-0.4.0.tar.gz |
|---|---|
| Size | 42.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
221987e3b3d843feaedef995ef1f72b29386134ea3ada832b35444b2995453c2
|
|
BLAKE2b-256 checksum How to use checksums |
c53d04222d7c0bef8da229288f1cc2fbc09ad5620de22721654b4f5d6e5aa4d2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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 Mar 27, 2026.
Transparency logRelease files / marimushka-0.4.0-py3-none-any.whl
| Download URL | marimushka-0.4.0-py3-none-any.whl |
|---|---|
| Size | 41.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3416b644a34bb3ac0466e8ac18bf857705fe05aca24193621a723af1c5e8d770
|
|
BLAKE2b-256 checksum How to use checksums |
2a2fd6d6878aa1c45ea8749f7bb3d133c0ebfd66c4d76198f19d00854b0ce528
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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 Mar 27, 2026.
Transparency log