inmake DOCS
Docs / Modules / Events and retries

Events and retries

Hook entries, the outbox with retries, dedupe on the receiver.

6.1 Hook entries#

Install writes one managed entry into the store's setup/hooks under the multi-event all key:

{id: `module_${id}`, name: `MODULE - ${id}`,
 description: 'Managed by the platform for an installed module - do not edit.',
 events: <kinds from manifest.events>, filter, mask: manifest.events_mask, on,
 collection: <union of per-event collection lists>,
 endpoint: `${endpoint}/event`}          // hosted/platform: the platform's own ingress leg (later phase)

The long form events: [{kind, collection, filter, on}] expands to one entry per distinct (filter, collection), suffixed module_{id}_{n} — a hook entry carries one filter map. The entry signs with the store secret hook_module_{id} = module_key, so one key signs events and every other envelope. The entry is upserted by id on install / update and every module_{id}* entry is removed on uninstall; setup/hooks is cached with a 3 s revalidation, so the next delivery sees a change at once. The rest of the delivery contract — payload shape, masking, on: create|update|delete, tombstones, the x-drop-* context headers — is the ordinary hook feed (docs/hooks-and-caching.md).

Vocabulary: orders, invoices, data, forms, files and the new module_records — a write to store/{sid}/modules/{id}/{collection}/{docId} (a records collection, §5.5; label modules/{id}/{collection}/{docId}), fired by the store write dispatcher in main and, through the shim, in a connected cloud. Writes to the install doc and to the reserved outbox/, state/ and logs/ fire nothing. templates / elements are render signals and never module events.

6.2 Retries — the outbox#

Per-store hooks are best-effort; module entries are retried. Two things differ for a module_* entry: the delivery carries the envelope header set (x-drop-module, x-drop-point: event, x-drop-store, x-drop-env, x-drop-request), and a failed delivery writes store/{sid}/modules/{id}/outbox/{request} = {label, endpoint, headers, body, attempts: 1, next_at: now + 5m, last_status, last_error, created_at}.

The outbox sweep (POST /system/module/outbox/sweep, master — no cron since 2026-10-24: the moduleOutbox schedule was removed, failed deliveries are still recorded and stamp health) re-delivers rows with next_at <= now (200 per store per run) with the stored headers — the same x-drop-request — and:

Attempt Wait before it
1 (the original) —
2 5 m
3 15 m
4 1 h
5 4 h
(6th failure) 12 h → dead: true

Five attempts ≈ 17 h. After the fifth failure the row is marked dead, install.health flips to failing with point: 'event'; dead rows are listed by POST /api/module/logs (the detail page's Logs tab) and purged 30 days after created_at. Success deletes the row.

6.3 Dedupe on the receiver#

Retries mean a non-idempotent receiver sees duplicates. Two headers make dedupe trivial: x-drop-request (identical on every retry of one delivery) and x-drop-updated (the doc's update time — drop anything older than what you already hold). expressModuleRouter dedupes x-drop-request by default with a 1,000-entry LRU; a module that keeps its own ledger (a records collection or state/) should key on the request id.