Validate & package
The packaging pipeline and the safety checks the backend runs on upload.
manifest ui/ worker/ tests/secrets .env executablesA module package is a .zip with hanabi.module.json at its root. This page is the focused reference for going from a folder on disk to an installed module. (For the whole build → publish → install lifecycle, see developer-guide.md.)
1. The folder layout #
my-module/ ├─ hanabi.module.json ← the manifest (REQUIRED, at the root) ├─ ui/ │ └─ index.html ← entrypoints.ui (a browser module's UI) └─ worker/ ← optional └─ worker.py ← entrypoints.worker (the server half)
entrypoints.uimust point inside the package (e.g.ui/index.html).- A browser-only module needs no
worker/. A worker-only utility still needs auientry today (it can be a tiny launcher page). - See
manifest-reference.mdfor every manifest field.
2. The hanabi CLI #
The tooling lives in packages/module-cli. Run it via the workspace script (no global install needed):
# scaffold a starter package npm --workspace packages/module-cli run hanabi -- module init "My Module" --dir ./my-module # print a manifest template you can copy npm --workspace packages/module-cli run hanabi -- module template # validate the folder against the manifest contract (no zip produced) npm --workspace packages/module-cli run hanabi -- module validate ./my-module # build the upload-ready zip (validates first) npm --workspace packages/module-cli run hanabi -- module pack ./my-module --out ./my-module.zip
The same validator runs in the CLI, the SDK (validateManifest), and the backend, so "valid locally" means "accepted on upload".
3. What module pack does #
- Validates the manifest + structure first; refuses to pack an invalid module.
- Strips junk —
.git,node_modules,.DS_Store,Thumbs.db, the output zip itself. - Rejects secrets + native executables — a
.env, key files,.exe/.dll, etc. fail the pack (a module is sandboxed web/Python, never a binary). - Writes the manifest at the archive root so the platform finds it (it won't accidentally nest your folder).
There's no artificial size limit on the package or its files.
4. Svelte (or other build-step) UIs #
A raw-HTML module (like every demo-*) packs as-is. A module whose UI is a Svelte app is first compiled to a single sandbox-safe ui/index.html by the bundler, then packed:
npm run module:build -- ./my-svelte-module # → writes ui/index.html npm --workspace packages/module-cli run hanabi -- module pack ./my-svelte-module --out ./my-svelte-module.zip
See broad-reader and svelte-scratchpad for Svelte examples.
5. Upload, review, install #
In the Toolbox: Module Store → Upload (or the Developer Portal for a draft you own), drop the .zip, and:
- A module with only baseline / consent capabilities can install + run immediately.
- A module that declares admin-approved capabilities (
network.fetch,worker.execute,jobs.schedule,client.wasm) is published via review. - A module that declares admin-HIGH capabilities (
worker.native,worker.service,network.fetch.broad, big worker tiers) additionally needs an admin to grant each one (with a quota) at review — they're default-deny. See the grant panel in the Developer Portal review queue, orPOST /developer/admin/modules/{id}/grants.
Troubleshooting #
| Symptom | Fix |
|---|---|
| "permission not allowed" on validate | the permission isn't in the registry allowlist — check spelling against manifest-reference.md |
| pack refuses a file | it looks like a secret or a native binary — remove it; modules are web/Python only |
| upload 400 "manifest not found" | hanabi.module.json must be at the zip root, not inside a subfolder — use module pack (it does this for you) |
| job fails "module does not provide a server worker" | declare entrypoints.worker + worker.execute, and the server needs the container backend (worker-sandbox-docker-setup.md) |
See also #
converting-apps.md— turn an existing HTML or Python app into a module.examples.md— runnable samples, one capability each.developer-guide.md— the full lifecycle, including versioning + branches.