Glossary
Plain-language definitions of every term these docs use.
Building a HanabiMatsuri module means meeting a few technical words. None of them are as scary as they look. Here's each one in one short paragraph, with a tiny analogy, and a link to the doc that goes deeper.
New to all of this? Read this top to bottom once — the terms are ordered so each builds on the last. Then keep it open in a tab while you follow examples.md.
Module
A module is a mini-app you build and install into HanabiMatsuri. HanabiMatsuri itself is a "desktop in your browser"; a module is one of the programs that lives on that desktop — a calculator, a note-taker, an image converter. Analogy: the platform is the phone; a module is an app you install on it. At its smallest a module is just three things: a manifesthanabi.module.json — the file that names your module and declares everything it needs., a UI, and (optionally) a workerAn optional Python program that does heavy or native work off the browser, in a sealed container.. See README.md and examples.md.
Manifest
The manifesthanabi.module.json — the file that names your module and declares everything it needs. is a single file named hanabi.module.json that sits at the root of your module. It's a declaration — it doesn't do anything, it describes your module: its name, its version, where the UI file is, which folders it owns, and which permissions it's asking for. Analogy: the label on a tin — it tells you what's inside before you open it. Every field is documented in manifest-reference.md.
UI / front-end
The UI (user interface), or front-end, is the part of your module a person actually sees and clicks — the buttons, text boxes, and pictures. For most modules it's exactly one file, ui/index.html, a normal webpage. Analogy: the dashboard of a car — the dials and pedals you touch, not the engine. The simplest possible example is examples/modules/hello-world-webpage.
Worker / back-end
The workerAn optional Python program that does heavy or native work off the browser, in a sealed container., or back-end, is an optional Python file that does heavy or private computing for your module on the server, out of sight. Most modules don't need one. You add a worker when the UI alone can't do the job — converting a video, crunching a big spreadsheet. Analogy: the kitchen behind a restaurant — you never see it, but it's where the cooking happens. The worker is a pure inputs → outputs transform: it takes what you hand it, returns a result, and that's all. It runs in a locked container with no network and no access to the database or anyone else's files unless specifically granted. See worker-guide.md. (You only need Python if you write one — see the FAQ.)
iframe
An iframe is a webpage embedded inside another webpage, in its own little frame. HanabiMatsuri runs your module's UI inside one. Analogy: a picture-in-picture box on a TV — a self-contained screen sitting inside the bigger screen. The point is isolation: your module gets its own frame and can't reach out and mess with the rest of the platform around it.
Sandbox
A sandboxThe locked-down frame your UI runs in — no cookies, no desktop, no network unless granted. is a safety boundary that lets code run but stops it from touching anything it shouldn't. Your module's iframe is sandboxed, so even buggy or malicious module code can't read the platform's data, your cookies, or other modules. Analogy: a children's sandbox — you can play freely inside it, but the sand stays in the box. This is the whole reason the platform can safely run code that strangers wrote. See the isolation summary in capability-api.md.
Opaque origin
An opaque originYour UI’s sandboxed identity: it belongs to nobody, so it can’t read the host page, cookies, or other modules. is the strongest version of that sandbox. Normally a webpage has an identity (its web address) that unlocks stored data, cookies, and saved files that belong to it. An opaque-origin iframe has no identity — so it has no cookies and no persistent storage of its own; nothing is "remembered" for it. Analogy: a hotel room with no name on the door and no key cut yet — you can use it right now, but it isn't yours and nothing stays in it after you leave. This is why localStorage silently fails to save in a module — see the FAQ.
postMessage
postMessage is the browser's built-in way for two separate frames to talk by passing notes, since the sandbox won't let them touch each other directly. Your module calls postMessage to send a request out of its locked box to the platform, and the platform answers the same way. Analogy: sliding a note under a locked door and waiting for one to come back. You rarely call it by hand — the bridge client (and the SDK) wrap it for you. The full message protocol is in capability-api.md.
The bridge (capability bridge)
The bridgeThe trusted layer that carries every request from your sandboxed UI to the platform, checking permissions. is the trusted code in the parent window that receives your module's postMessage notes and decides what to do with them. For every request it checks: is your module asking for something its manifest declared? Did the user grant it? Is the file you named inside your own scopeThe rule that file operations stay inside your own Modules/<Name>/ folders for the current user.? Only if all three pass does it act. Analogy: a bank teller behind glass — you slide a request through the slot, they verify you're allowed, and they fetch what you asked for; you never reach into the vault yourself. Your module talks to the bridge with a tiny copy-paste bridge client (~12 lines: a call(type, payload) function). See capability-api.md and the three checks it runs in capability-api.md.
Permission vs capability
A capability is a thing a module can do — read a file, show a notification, run a worker, reach the network. A permission is the line in your manifest that asks for one. So you write a permission (e.g. files.read) to unlock a capability. Analogy: a capability is a door in the building; a permission is the keycard you request for it. Permissions come in tiers by how sensitive they are: baseline (on the moment you install), consentA one-time prompt the user approves to allow an elevated permission like files.read.all. (the user is asked once), admin (switched on when an admin reviews your module before it's published), and admin-HIGH (an admin must individually flip it on and set a usage limit — off by default). The full tier table is in capability-api.md.
Scope
ScopeThe rule that file operations stay inside your own Modules/<Name>/ folders for the current user. is the boundary of what your module is allowed to touch. By default a module can only read and write its own folders (under Modules/<Your Module>/) — never the Desktop, never another module's files, never another user's data. To reach beyond your own folders you must ask for a broad permission like files.read.all, which the user has to consent to. Analogy: a hotel keycard that opens only your room — not the whole floor. Stepping outside your scope is error HANABI-F002; see the FAQ.
JSON
JSON (JavaScript Object Notation) is a simple, strict text format for writing down structured data — names paired with values, wrapped in { }. Your manifest is JSON, and data you send across the bridge is JSON. Analogy: a fill-in-the-blanks form — every field has a label and a value, and the punctuation has to be exact. "Strict" matters: one missing comma or quote and the file won't load (that's validation error HANABI-V001). A tiny example:
{ "name": "My Module", "version": "1.0.0" }
async / await & Promise
When your module asks the platform for something, the answer doesn't come back instantly — the note has to travel to the bridge and back. A Promise is JavaScript's IOU for "an answer that's coming." `await` means "pause here until the IOU is paid," and a function that uses await is marked `async`. Analogy: ordering at a counter — you get a buzzer (the Promise) now, and `await` is you waiting for it to buzz before you eat. You'll see this in every example:
const file = await hanabi.readFile(id); // wait for the file, then continue
base64
base64 is a way of writing binary data — the raw bytes of an image, a PDF, a spreadsheet — as plain text made of ordinary letters and digits. The bridge passes files as base64 text because notes between frames are text, not raw bytes. Analogy: spelling a word out loud over the phone ("B as in bravo…") so it survives a channel that only carries speech. You'll see field names like bytesB64; convert with the browser's built-in btoa() / atob(). It makes the data a bit bigger, but it always arrives intact.
REST API / endpoint
A REST API is the menu of web addresses the platform's server understands, and each individual address on that menu is an endpoint (e.g. /auth/me). When the bridge fulfils your request, it calls these endpoints on your behalf, with your signed-in session attached — your module never calls them directly. Analogy: the bridge is the waiter who walks your order back to the kitchen; the endpoints are the items on the kitchen's order pad. The platform's own API contract lives in ../api.md.
CSP (Content-Security-Policy)
A CSP is a rule list, sent with your module, that tells the browser exactly which outside places your page is allowed to load things from or talk to. The platform locks yours down hard: by default your UI can't fetch from any website it didn't declare. Analogy: a guest list at the door — if a web address isn't on the list, the browser turns it away. This is why a stray fetch() to some site fails until you declare that site in your manifest — see the FAQ. The exact policy is described in capability-api.md.
NDJSON
NDJSON (newline-delimited JSON) is a list where each line is its own complete JSON object, one per line, separated by line breaks. The platform uses it for streaming — sending you results one at a time as they're ready, instead of making you wait for the whole batch. Analogy: a ticker tape — each line prints and is readable on its own, the moment it's done. You meet it with streaming jobs and moduleStream; see capability-api.md.
Package / zip
A package is your finished module folder squeezed into a single .zip file so it can be uploaded and installed. You don't zip it by hand — the hanabi CLI does it, and while packaging it also checks your module and strips anything that shouldn't ship (like secret files). Analogy: boxing up a parcel for the post — everything needed goes in one sealed box with a label on top. The label on top is your hanabi.module.json. See packaging.md; rejection reasons are the V codes in error-codes.md.
The SDK (@hanabi/module-sdk)
The SDK (software development kit) is an optional, ready-made helper library that wraps the raw bridge messages in tidy, typed methods — so you write hanabi.readFile(id) instead of hand-assembling a postMessage. Analogy: a TV remote instead of reaching inside the set — same result, far fewer ways to get it wrong. It speaks the identical protocol as the copy-paste bridge client, so you can start with the 12-line client and graduate to the SDK later. See capability-api.md.
Error code
Every error the platform (or your module) can hit has a stable code like HANABI-F002 — a short label that never changes, so you can search for it, look it up, and know exactly what went wrong and how to fix it. Analogy: a recipe-book index — instead of re-reading everything, you flip straight to the page for your problem. When something breaks, the developer debugger shows you the code; the full list, with a fix for each, is in error-codes.md.
Where to go next #
- New here? Follow the beginner path in examples.md.
- Hit an error? Look up its code in error-codes.md.
- Tripped on a common gotcha? See the beginner FAQ.
- Ready for the full message + permission reference? capability-api.md.