codex export
Build VST3 and standalone targets from your manifest, plus platform requirements and what lands in build/.
The
codexCLI is proposed, not yet shipped. This page describes the intended command surface; today the equivalent steps run through the workspace scripts andplugin-shell-kit generatedirectly.
Usage
npx codex export vst3
# → build/MyPedal.vst3
# → build/MyPedal.app (standalone, same kernel)
Two artifacts, one command. The .vst3 is the plugin you drop in your plugins
folder; the .app is a standalone macOS host that opens your UI over a CoreAudio
device and runs the identical kernel, which makes it the fastest way to check a
build without launching a DAW.
What actually happens
No magic, so here is the pipeline in full.
- Your manifest is read.
plugin.config.jsonnames the plugin, its vendor, its category, which kernel to compile, and where your built web UI lands. - The kernel is compiled twice from one source — freestanding WASM for the browser worklet, and a native library for the plugin. Same TypeScript, same math, same state layout.
@codex-music/plugin-shell-kitgenerates the shell. From your schema it emits the VST3 parameter table with stable indices, so automation survives version bumps; the state-chunk codec that saves and restores your patch inside a session; thecodexHostbridge JS injected before your bundle loads; and the CMake targets.- CMake builds the native shell, embedding the compiled kernel as the processor and your web UI bundle as the plugin editor.
Step 3 is worth pausing on, because it is what makes step 4 generic. There is one
shell, reused by every plugin, parameterized entirely by your manifest and
schema. The VST3 class ids are derived deterministically from your plugin id —
same id, same ids, every regeneration — which is what lets a session saved
against one build reopen against the next.
Your manifest
Short, because most of it is derivable:
{
"id": "music.codex.my-pedal",
"name": "My Pedal",
"vendor": "Codex Music",
"version": "0.1.0",
"category": "Fx",
"kernel": {
"package": "@codex-music/audio-kernels",
"profile": "profiles/tone-pedal.profile.json",
"abiPrefix": "sctone_"
},
"ui": {
"dist": "dist-plugin"
}
}
category is "Fx" or "Instrument". kernel.abiPrefix must match the
kernelId in your ParamSchema; the generator fails loudly if they disagree,
rather than producing a shell wired to the wrong symbols. ui.dist is the Vite
output directory of your plugin-flavored build, bundled into plugin resources and
served to the embedded webview through a custom scheme handler.
The schema itself is found automatically at src/schema.ts, with the export
detected as the single one shaped like a ParamSchema whose kernelId matches
your abiPrefix. If a module exports more than one, add a schema block naming
the module and export explicitly.
Platform requirements
V1 is VST3 plus standalone on macOS. That is the shipped scope, not a stylistic preference — it is where the shell, the validator run, and the parity harness are green today.
Exporting needs a native toolchain that the dev and browser loops do not: a C++ compiler, CMake, and the VST3 SDK, which CMake fetches for you. The SDK is dual-licensed GPLv3 or a free proprietary agreement from Steinberg; which one applies to you depends on how you license your plugin, and Licensing covers the interaction.
AU and CLAP are on the roadmap. Because the shell is generated from your manifest, they arrive as a new export target rather than a rewrite of your tool — your kernel, schema, and UI are unchanged, and the format entry point is the only new native code, written once by us and shared by every plugin.
What is real today
The parts this command would wrap already exist and work. The native plugin shell
is built and passes the Steinberg VST3 validator. plugin-shell-kit generate is
a real CLI that reads a real plugin.config.json, loads your schema, and emits
the param table header, the CMake fragment, and a resolved manifest snapshot.
There is a verification target that loads the built .vst3 through the VST3
hosting API, replays a golden fixture with a preset chunk and an automation
point, renders offline, and byte-compares the result against the offline batch
render — including under 512-frame host chunking.
What does not exist yet is the single-command wrapper. Today those steps are run individually through the workspace scripts. See Plugin Shell Kit for the generator as it stands, and Parity and determinism for what the byte-compare guarantees.
When the build is green, codex publish puts it in the
directory.