Publishing a node-pack
Once your .nodepack.json validates locally (every kind shows in the picker + compiles cleanly), publish to the Hub for others to install. Two paths — local-test then publish, or smoke-test in a fresh project first.
Local test loop
The fastest iteration loop bypasses the Hub entirely:
1. packs.node.install { manifest: <your full .nodepack.json object> }
2. packs.list { kind: "node" } → confirm packId, isBundled:false
3. documents.add { type: "builder", title: "Pack test" } → module_id
4. builder.graph.node.add { module_id, kind: "<your_kind_id>" } → node id
5. builder.graph.node.add { module_id, kind: "output" } → out id
6. builder.graph.connect { module_id, source_node_id: <yours>, target_node_id: <out>, target_port: "in_l" }
7. dsp.compile { module_id }
8. builder.source.get { module_id } → verify your toBox emits what you expect
(Not a click-chain — steps 1, 4 and 6 need YOUR pack's manifest + kind ids; ask the assistant to run the loop against your file.)
If compile fails, the error string includes the libfaust diagnostic — usually a missing param ref, an arity mismatch, or a typo in a lib() call. Fix the .nodepack.json, packs.node.install again (writes overwrite atomic), retry.
Publishing to the Hub
When the local loop is happy:
hub.publish {
kind: "node-pack",
file_path: "asset:assets/my-pack.nodepack.json",
slug: "my-pack",
title: "My Pack — short tagline",
description: "Longer description for the Hub item page."
}
The Hub backend's NodePackInspector validates the manifest server-side + extracts derived metadata (kindCount, packId, ownerHandle). On success you get back the item's id — record it for follow-up version uploads.
Subsequent versions
hub.version.upload {
kind: "node-pack",
item_id: "<id from the publish response>",
file_path: "asset:assets/my-pack-v2.nodepack.json"
}
The Hub computes the new version number automatically (existing-version + 1). The local pack store refuses to downgrade (incoming version must be ≥ installed) — bump the version field in the manifest on every upload.
Forking + iterating
Hub items can be forked: hub.fork { item_id } creates a copy in your namespace. Useful for "I want to base my pack on faust-vocal but add a new vocal effect" — fork, edit, publish under your handle.
Kb-pack-style packs as a doc surface
If your node-pack covers a meaningful domain (a vocal effects suite, a granular synth toolkit, a vintage gear bundle), pair it with a book documenting the kinds. The book lives separately on the Hub (hub.publish kind:"book") but Users find both when searching for the domain.
The Book Reader module's tutorial blocks can link to your kinds with companionPatches references + assistant hints. See FaustWave — Hub §3 for the publish flow.
Bundling for FaustWave team packs
If you're shipping a FaustWave-team pack that should auto-restore on launch:
- Drop the
.nodepack.jsoninpackages/app/build-resources/bundled-node-packs/. - Add the packId to
BUNDLED_PACK_IDSinpackages/app/electron/node-pack-store.ts. - Bump the
versionfield —restoreBundledPackscompares against installed and only re-writes when the bundled file is newer.
Community packs DON'T go in BUNDLED_PACK_IDS. They stay Hub-only — users install them on demand.
Common rejection paths
The validator + adapter at install time check for the most common authoring traps. If any fail, the install rejects + emits a per-line error in Logs:
- Unknown port
idreferenced frominput('xyz')— the input portxyzdoesn't exist ininputs[]. - Unknown param
idreferenced fromparam('xyz')— same for params. - Forward def ref —
def('A')referenced beforedefs.Ais declared (defs resolve in declaration order). - Arity mismatch in
seq(a, b)—a's output arity !=b's input arity. - Missing
toBox.outputs.<port>— every output port needs a key. Sinks (zero outputs) use the reserved__processkey. - Bare-name kind not in scope —
prim('XYZ', …)whereXYZisn't in stdfaust + the kind doesn't list the relevant library inimports. Add the lib to the kind'simportsfield. importsentry not ending in.lib— the validator rejects entries that don't end in the.libsuffix.
Where to read more
- Source of truth —
packages/extension-dsp-builder/src/lib/packKindData.tscarries theNodeKindDatavalidator + thevalidateNodeKindData()function that runs at install time. The validator's error strings are precise; read it when in doubt. - Adapter —
packKindAdapter.tswalkstoBoxand produces the runtimeNodeKindDef. Read it to understand the evaluation order + the substitution env. - Examples — every file under
packages/app/build-resources/bundled-node-packs/is real production code. Look at the simpler ones first (faust-physical-models,faust-spatial) for clean templates. - Architecture —
docs/NODE-PACK-ARCHITECTURE-ZIEL.mdfor the bundled-vs-Hub classification and the long-term direction.