Compare commits

...
Author SHA1 Message Date
Waters ChurchandClaude Fable 5.1 07e4c78417 Module rules: a native helper ships prebuilt, for Apple silicon and Windows
The machine running ProdDash is a packaged app or the Windows bundle plus
modules from the admin page's catalog. It has no compiler, and the catalog
delivers exactly the folder that is in the repo — so a module's native
program (audio capture, a decoder) ships already built, inside the module
folder, one file per platform: bin/<name>-macos-arm64 and
bin/<name>-win-x64.exe are required, Intel macOS optional, source and the
script that rebuilds every binary under native/.

The guide's rule 3 now says so, "The shape of a module" shows the layout,
and a new "Native helpers" section gives the contract: pick the binary by
platform at runtime, report the feature off when there is none — never
build, never search the machine, never ask the operator to install
anything — one command-line contract in any language per platform, bump
the version when a binary changes, keep them lean, sign the macOS one.
CLAUDE.md carries the one-line version. Rules only: no module changes here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 16:34:34 -04:00
2 changed files with 74 additions and 2 deletions
+6 -1
View File
@@ -28,7 +28,12 @@ title-bar controls). The rules that matter most:
ship inside the module folder** (zero deps is the norm). **Everything the ship inside the module folder** (zero deps is the norm). **Everything the
module does runs inside its server entry** — no helper scripts, companion module does runs inside its server entry** — no helper scripts, companion
programs or daemons the operator starts by hand; a listener or decoder is programs or daemons the operator starts by hand; a listener or decoder is
started in `init()` and stopped in `stop()`. Declare `"proddash": ">=x.y.z"` started in `init()` and stopped in `stop()`. **A native helper ships
prebuilt inside the module folder for Apple silicon macOS and 64-bit
Windows** (`bin/<name>-macos-arm64`, `bin/<name>-win-x64.exe`, source and
build script under `native/`); nothing is ever compiled on the machine
running ProdDash, and a missing binary means the feature reports itself
off, not a broken module. Declare `"proddash": ">=x.y.z"`
and bump the module's `version` on every change. and bump the module's `version` on every change.
- Browsers never call upstream services directly; the module's server routes - Browsers never call upstream services directly; the module's server routes
proxy everything (no CORS, secrets stay server-side). proxy everything (no CORS, secrets stay server-side).
+68 -1
View File
@@ -28,7 +28,10 @@ people running the booth depend on them.
ProdDash. If your feature needs a listener, a decoder or a worker, your ProdDash. If your feature needs a listener, a decoder or a worker, your
server entry owns it: start it in `init()`, stop it in `stop()`, report server entry owns it: start it in `init()`, stop it in `stop()`, report
it in `health()`. A child process the module itself spawns and supervises it in `health()`. A child process the module itself spawns and supervises
is fine; a program someone launches by hand is not. is fine; a program someone launches by hand is not. **A native helper —
anything that is not JavaScript — ships prebuilt inside the module folder,
for Apple silicon macOS and 64-bit Windows; nothing is ever compiled on
the machine running ProdDash** (see "Native helpers" below).
4. **Declare the ProdDash version you need** (`"proddash": ">=1.1.0"` in the 4. **Declare the ProdDash version you need** (`"proddash": ">=1.1.0"` in the
manifest) and **bump your own `version`** whenever you change the module. manifest) and **bump your own `version`** whenever you change the module.
The admin page refuses to install a module whose requirement the running The admin page refuses to install a module whose requirement the running
@@ -83,6 +86,10 @@ modules/<id>/
├── module.json manifest (required) ├── module.json manifest (required)
├── client.js ES module, client entry (required) ├── client.js ES module, client entry (required)
├── server.js server entry (optional — only if you need server code) ├── server.js server entry (optional — only if you need server code)
├── bin/ native helpers, prebuilt per platform (only if you need one)
│ ├── <name>-macos-arm64
│ └── <name>-win-x64.exe
├── native/ their source and the script that builds every bin/ file
└── ... assets (css, images…) served statically at /modules/<id>/ └── ... assets (css, images…) served statically at /modules/<id>/
``` ```
@@ -97,6 +104,66 @@ the newer version runs. **Uninstall** in the admin page removes an installed
module's files (a bundled one is hidden instead, since its folder belongs to module's files (a bundled one is hidden instead, since its folder belongs to
the app); its settings are kept for a later reinstall. the app); its settings are kept for a later reinstall.
### Native helpers
Some features need a program JavaScript cannot be: reading an audio input,
decoding timecode, measuring sound. Rule 3 says the module owns that program;
this section says how it ships: **already built, for every platform it runs
on, inside the module folder.** The machine running ProdDash is a packaged
app or the Windows bundle plus modules from the admin page's catalog. It has
no compiler and never will — the catalog delivers exactly the folder that is
in the repo, so the binaries live in git next to the code.
- **Platforms.** Apple silicon macOS and 64-bit Windows are required. Intel
macOS is welcome but optional. One file per platform in `bin/`, named for
what it is:
| Platform | File |
| --- | --- |
| macOS, Apple silicon | `bin/<name>-macos-arm64` |
| Windows, 64-bit | `bin/<name>-win-x64.exe` |
| macOS, Intel (optional) | `bin/<name>-macos-x86_64` |
- **Pick it at runtime, and cope without it.** Choose by `process.platform`
and `process.arch`; when there is no file for this machine, the feature
says so — in `health()` and in the tile's `setStatus` — and everything else
in the module keeps working. Never try to build it, never look for a copy
elsewhere on the machine, never tell the operator to install anything.
```js
const HELPERS = {
darwin: { arm64: 'ltc-capture-macos-arm64', x64: 'ltc-capture-macos-x86_64' },
win32: { x64: 'ltc-capture-win-x64.exe' },
};
const name = HELPERS[process.platform]?.[process.arch];
const helper = name ? path.join(__dirname, 'bin', name) : '';
if (!helper || !fs.existsSync(helper)) {
// Timecode is off on this platform. Say it once, keep the clock running.
}
```
- **One contract, any language.** Every platform's binary speaks the same
command line and output format, so `server.js` has one code path. What is
behind it is your choice per platform — Swift against CoreAudio on macOS,
C#, C or Rust on Windows — the module does not care.
- **Source and build beside it.** Keep the source under `native/` with one
script that produces every `bin/` file from a checkout, so any binary can be
rebuilt from what is committed. A helper that cannot be rebuilt does not
ship.
- **Bump the module version whenever a binary changes**, like any other
file — that is how installs learn about it.
- **Keep them lean.** Optimised builds, no debug symbols, a few megabytes at
most: the catalog fetches the whole repo as one archive, capped at 80 MB.
- **Signing.** Apple silicon refuses unsigned code, so sign the macOS binary
— Developer ID when you have it, ad-hoc at least. Windows needs nothing:
files the app writes carry no download mark, so SmartScreen never sees
them. Permissions come from the launcher (see "Permissions macOS has to
grant"), not from the binary.
A helper that ends up existing for one platform only is an exception made
on purpose, not a default: say so in the module's README and in the health
line, so a Windows booth knows why the feature is off.
## module.json ## module.json
```json ```json