Hanabi Developer Hub
/ Ship / Validate & package
Ship

Validate & package

The packaging pipeline and the safety checks the backend runs on upload.

module/
validate + safety scan
module.zip
Packed
manifest ui/ worker/ tests/
Stripped / rejected
secrets .env executables
module pack produces an upload-ready, scanned zip.

A 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 #

text
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.ui must point inside the package (e.g. ui/index.html).
  • A browser-only module needs no worker/. A worker-only utility still needs a ui entry today (it can be a tiny launcher page).
  • See manifest-reference.md for every manifest field.

2. The hanabi CLI #

The tooling lives in packages/module-cli. Run it via the workspace script (no global install needed):

bash
# 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 #

  1. Validates the manifest + structure first; refuses to pack an invalid module.
  2. Strips junk.git, node_modules, .DS_Store, Thumbs.db, the output zip itself.
  3. Rejects secrets + native executables — a .env, key files, .exe/.dll, etc. fail the pack (a module is sandboxed web/Python, never a binary).
  4. 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:

bash
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, or POST /developer/admin/modules/{id}/grants.

Troubleshooting #

SymptomFix
"permission not allowed" on validatethe permission isn't in the registry allowlist — check spelling against manifest-reference.md
pack refuses a fileit 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 #

Live manifest validator valid
Edit the manifest
Checks (same as the backend)
V002 schema_version is "5.0"
V003 id is a valid slug
V004 name, version, and entrypoints.ui are present
V005 all permissions are recognised
V006 filesystem.root is under Modules/
V011 ui.window sizes are consistent