← Addonium

9. Compiled Module Contract (Locked addons only)

A compiled module is a single script - plain JS, or a bundled/minified/ lightly-obfuscated variant - that a host loads in-process instead of calling over HTTP. This is the shape 8SPINE popularized. In Addonium it is reserved for Locked Addons because it's the distribution format best suited to shipping something a bare URL can't casually leak: the code can require a key before it will do anything useful.

9.1 File layout

myaddon.aium         # the module - plain text, may be minified. Safe to share.
myaddon.aiumkey      # the unlock key - one per license/user. Keep private.
myaddon.manifest.json  # OPTIONAL standalone copy of the manifest for preview

.aium files are just JavaScript with a documented export shape (below). The extension exists purely so hosts and file pickers recognize them; a host MAY equally accept a plain .js/.mjs file.

9.2 Module shape

export const ADDONIUM_MODULE = {
  // -- Identity (mirrors manifest.json; used if no standalone manifest ships) --
  manifest: {
    addonium: "1.0",
    id: "com.example.locked-source",
    name: "Example Locked Source",
    version: "2.0.0",
    type: "locked",
    resources: ["search", "stream"],
    auth: { method: "module-key" }
  },

  // -- Lifecycle --
  // Called once at load time with the key the host obtained from the user
  // (pasted string, .aiumkey file contents, or QR-scanned payload).
  // Must return true/false (or throw) - this is the module's own gate,
  // not something the host can bypass.
  async unlock(key, context) {
    return verifyKeyAgainstSelf(key); // author-defined; opaque to host
  },

  // -- Core methods (same semantics as the HTTP endpoints in §8) --
  async searchTracks(query, limit, settings) { /* ... */ },
  async getTrackStreamUrl(id, quality, settings) { /* ... */ },

  // -- Optional methods --
  async getAlbum(id) { /* ... */ },
  async getArtist(id) { /* ... */ },
  async getPlaylist(id) { /* ... */ },
  async resolve({ isrc, title, artist, durationMs }) { /* ... */ },
};

export default ADDONIUM_MODULE;

9.3 Loading procedure (host-side)

  1. Host reads the .aium file as text.
  2. Host reads/asks for the corresponding key (paste, file import, or QR).
  3. Host evaluates the module in an isolated/sandboxed context (a Worker, a vm context, a new Function with no ambient host globals - implementation is host-specific, but it MUST NOT be run with full app privileges).
  4. Host calls unlock(key, context). If it resolves falsy or throws, the module is treated as not installed - no further methods are called, and no partial functionality is granted.
  5. Once unlocked, the host proxies calls the same way it would to an HTTP addon: searchTracks/search, getTrackStreamUrl/stream/:id.

9.4 Author's discretion on strictness

The author decides how the module behaves without a valid key. Options include (non-exhaustive, and entirely up to the author's own code):

  • Refuse to execute at all (unlock throws).
  • Execute but return empty/degraded results (a "demo mode").
  • Execute fully for a trial window embedded in the module's own logic.

Addonium does not mandate any of these - it only guarantees that unlock is always called first and that a host must not skip it.

9.5 Integrity & trust

Because a compiled module runs host-side code rather than being called over the network, hosts SHOULD:

  • Sandbox execution (no filesystem, no arbitrary network beyond what a fetch-like binding explicitly allows).
  • Surface the module's declared id, author, and version to the user before granting the sandbox a network binding.
  • Treat a module's self-reported manifest as informational, not a security boundary - sandboxing is what actually protects the host, the manifest is just what the user is told.