inmake DOCS
Docs / Modules / Overview

Overview

What a module is, the three hosting tiers, the parts of a module and what the platform guarantees.

A module adds behaviour the platform does not ship to a project: a carrier, an invoicing, payment or order-fulfilment provider, a booking system, a sync with a CRM or a newsletter tool, an admin page or widget of your own. Modules are installed per project from the console's Modules area, and every module — yours or the platform's — goes through the same contract.

This reference is that contract. It is written for module authors — a developer building a module for their own projects or for a client — and for the project managers who install, configure and update modules (§8 Lifecycle, §9 The console). Each section stands on its own; a §n reference points at the page of the same number in this guide.

What a module can do

  • Check or annotate an order at checkout, before it is written (order.validate, §4).
  • Plug into a whole core contract as a carrier, an invoicer, a payment gateway or an orders fulfilment provider — the project then picks the module wherever it would pick a built-in provider (provides, §2.4).
  • Receive the project's writes as signed events, retried when a delivery fails (§6), and third-party webhooks through the platform without exposing its own address (routes.inbound, §4).
  • Serve endpoints on the project's own website under /m/{id}/** (routes.web, §4).
  • Add configuration fields, forms, buttons, cards, full admin pages and Overview widgets to the console (§4.2, §4.3).
  • Keep its own content collections, private records and state (§2.1, §5.5), ship element kits, e-mail templates and translations (§7), and run on a schedule (§4).

Where it runs — the three hosting tiers, compared in §1: connected (your own HTTPS endpoint — or Cloud Functions the platform deploys into your space's own Firebase project for you, §8.7), hosted (a Wasm package on the platform's module runner — you ship code, not a server, §8.8) and platform (the same format, published by the platform for every project).

Modules or services. A module is a product projects install. For a small backend that belongs to one site — a few routes calling another system with a key — write a store service instead: one JS file at /fn/{id}/**, no manifest, no install.

Before you start

  • A project's plan decides whether it may install modules, which tiers, and how many (§5.1); a refused install names the plan and the limit.
  • hosted and platform modules need the environment's module runner; where none is configured, installing one answers hosting_unavailable.
  • A developer with the developer platform role submits a package for their organization and it waits in review until a platform admin approves it; a platform admin's upload is published at once (§7.2 "Publishing").
  • Build and check a package locally first — the starter's module:pack runs the same checks as the upload (§7.2, §7.3), and §11 shows how to test a module end to end.

Contents: 1 What a module is · 2 The manifest · 3 The envelope · 4 Points · 5 Identity and access · 6 Events and retries · 7 Packages · 8 Lifecycle · 9 The console · 10 Where things live · 11 Testing a module locally.


1 What a module is#

A module is a manifest (ModuleManifest, core-lib entities/module.ts) plus code somewhere. The manifest declares what the module uses; the install decides what it gets. Where the code runs is the hosting tier:

Tier Who writes the code Where it runs How the platform reaches it How it reaches the platform
connected the client (or an agency) their own Firebase project as Cloud Functions gen2, or any HTTPS host of theirs signed POST {endpoint}{path} (the envelope, §3) the SDK with the install's scoped store token
hosted the client a Wasm package on the platform's module runner (a separate project, drop-core-modules) the gateway invokes the runner with the envelope + the install token host functions only — the token is injected per call; the module never sees it
platform the platform the same runner, same package format, published by master / administrator, run with the platform limits profile the gateway invokes the runner host functions, with a platform-profile token per install
none — an elements module the client, an agency or the platform nowhere on the platform: its kits' elements and global css / js run in the shopper's browser on the pages that place them never — no endpoint, no runner; every routing entry point answers 404 module_static never — it has no token (§2.1 "hosting: 'none'")

One gateway in the core API makes the tiers interchangeable: a store installs a module without knowing which tier it is; a client module can move between hosted and connected (the starter ships an Express router and an Extism entry over the same handler object); a platform module is a client module with a different publisher and profile. Every platform → module call crosses the gateway; every module → platform call is an ordinary API call on the install token, evaluated like any other store token.

A connected module is therefore: an HTTPS endpoint that verifies the envelope on every request, a handler per point it declares (§4), and the SDK (drop-core-sdk) started with the install token it received at install (§5). The package it ships is usually manifest.json alone (§7).

An elements module (hosting: 'none') is the opposite end: a package of element kits plus their shared global css / js and nothing else — no code the platform runs, no token, no events, no config, no console pages. A store mounts it from the catalog and gets the elements in its library; updates and uninstalls work as for every module-owned element (§8.2a). It needs no modules plan feature and counts against its own cap (§8.2).

What a module can never do (the security model's checklist, in author terms):

  • Run in a process or project that holds a master token — never the console server, never the API function, never the tenant fleet. A connected module is an untrusted, internet-reachable endpoint from the platform's point of view, even when it is deployed into the organization's own cloud.
  • Hold a token that is not role: service, revealable: false, access ∩ MODULE_MAX_ACCESS, with a validated scope and no customer (a module never spends a wallet: every paid route answers 400).
  • Read another party's secrets: with secure.secret granted, a module token reaches module_{id}_* keys only and key: '*' is refused.
  • Reach setup/hooks, setup/smtp, setup/payments, setup/analytics, members, promo/incore or system/** — the hard deny list (§5).
  • Write outside its namespace: modules/{id}/**, {entity}.modules.{id}, the collections it registered (tagged module), files under files/modules/{id}/**.
  • Receive the install token before its endpoint is proven (the two-step handshake, §4), or have the platform fetch anything from its endpoint to decide what it may do — the manifest comes only from the uploaded package.
  • Change its own grants: a version whose access / scope widen waits for a store manager's explicit accept (§8).
  • Escape the gateway's deadlines, size caps, SSRF guard and breaker (20 consecutive failures on a point → health.failing, the point short-circuits to its fallback).
  • Have its /secure/access token edited or deleted directly: managed tokens (note MODULE - {id}) change only through install / reroll / uninstall (409 managed_token).

Every install / configure / enable / uninstall / reroll / update and every declarative action lands in the store's activity feed (module id + path + entity id, never values); every gateway call is a drop.stat row.