Bundle System Overview
The application installs its built-in Bundle through the same runtime contract:
Terminology
- Bundle: the only installable unit. It contains at least one Provider.
- Provider: a Menu destination that loads a service or an app-owned view.
- Plugin: optional behavior attached globally, to selected Providers, or to the Provider directory that owns it.
Bundle
├── Provider[] one or more
│ └── Provider Plugin[] zero or more; owner-scoped
└── Bundle Plugin[] zero or more; global or providerIds-scoped
Implementation status
| Capability | Status |
|---|---|
@provider and @plugin metadata |
Implemented |
defineBundle, defineProvider, and definePlugin |
Implemented |
| One directory and manifest per Provider and Plugin | Implemented |
| Provider-scoped and Bundle-level Plugin activation | Implemented |
| Atomic registration and API/version validation | Implemented |
.kawai installation without rebuilding Kawaikara |
Implemented |
| Per-Provider Session isolation and shared profiles | Implemented |
| Locale and shortcut contributions | Implemented |
| Permission display and manifest consistency checks | Implemented |
| Runtime capability sandbox | Planned |
| Publisher signatures, updates, uninstall, and rollback | Planned |
| Provider/Plugin PluginView panels | Implemented with sandboxed HTML or app-owned internal views |
The application installs its built-in Bundle through the same runtime contract:
const host = new PluginHost(siteManager);
host.install(builtinBundle);
Registration model
Decorators store immutable class metadata under shared symbols. A class becomes
discoverable only when its directory manifest is loaded and a BundleDefinition
explicitly includes it. Registration never depends on import order or a global
reflection scan.
SiteManager.registerBundle() stages Providers, Plugins, and browser profiles.
It commits them only after every ID, manifest, permission, and profile reference
passes validation.
Plugin activation
Plugins are instantiated for the active Provider, never once for the whole app:
- Bundle-level Plugins with no
providerIdsactivate for every Provider. - Bundle-level Plugins with
providerIdsactivate only for matching IDs. - Plugins listed by a Provider manifest activate only for their owner.
- Matching Plugins activate before
Provider.load()so they can subscribe to events before the initial navigation. - On transition, Plugins deactivate in reverse order, then the Provider unloads.
Browser profiles
Each Provider uses its own persistent Electron Session by default. A Bundle may declare shared browser profiles, and a Provider may choose one as its default. Users can override assignments in Preferences or create their own profiles.
Multiple Providers assigned to the same profile share cookies and storage but
retain separate live WebContents. DRM Providers warn when the user assigns
shared state.
Locale and shortcuts
The app locale is resolved against Bundle and Provider locale declarations. The
result is exposed through context.locale.app, .plugin, and .site, and the
Provider locale supplies Accept-Language after Provider header hooks run.
Providers declare default accelerators in shortcut.defaultKey. Preferences
stores only overrides; an empty override disables the shortcut.
Permission declarations
| Permission | Declared use |
|---|---|
navigation |
Load a remote URL |
internal-view |
Load an app-owned internal view |
plugin-view |
Add sandboxed panels to the shared Menu PluginView area |
script-injection |
Execute code in a remote document |
cookies |
Transfer authentication cookies |
network-interception |
Transform requests or headers |
external-browser |
Start a separate login browser |
Each Provider manifest declares its runtime permissions. The top-level Bundle manifest must include their union and remains the user-facing installation consent boundary. Permissions are displayed during inspection but do not yet sandbox arbitrary module initialization.
Authoring principles
- Depend on
@kawaikara/site-api, not app Main or Electron internals. - Keep selectors, OAuth domains, user-agent workarounds, and PiP route guards in the Provider that owns them.
- Treat Provider and Plugin instances as single-use.
- Dispose listeners, timers, and injected state during unload/deactivation.
- Make injection idempotent and keep secrets out of logs.
PluginView panels
Providers declare panels in menu.panels; Plugins declare them in
@plugin({ panels }). Each panel has a stable id scoped to its owner, a
localized title, an order, and either an app-owned internal view or sandboxed
html. If one panel is available it fills the PluginView area. Two or more
panels get a browser-like tab strip.
Visible titles are allowed to repeat. Runtime identity is composed as
provider:<provider-id>:<panel-id> or plugin:<plugin-id>:<panel-id>, so a
rename or duplicate label does not break selection. Sandboxed documents have
no Kawaikara IPC and no same-origin access to the overlay Renderer.