Docs / Nebula Sound System documentation / Architecture and decisions

Architecture and decisions

Why Nebula is built this way, and the decisions it will not reverse — including why VST3 cannot be supported and CLAP was chosen instead.

Nebula's big choices are written down as numbered decision records: the situation, the options considered, the decision and its consequences. This page is that list, in the same order as the repository's docs/adr/ folder.

Two of them are the ones users ask about most, because they explain what the app will never do:

  • VST3 is not supported, and cannot be. The VST3 SDK is dual-licensed GPLv3-or-Steinberg-proprietary, and a GPL dependency cannot go into a binary shipped under the Functional Source License (FSL-1.1-ALv2). The dependency ledger once carried a row claiming the VST3 SDK is MIT; that row was wrong and has been corrected. Nothing in the repository links or ships the VST3 SDK.
  • CLAP was chosen instead, because it is licence-clean: MIT headers, a C ABI with no runtime to link, documented search paths and an open validator. A hosted plug-in's parameters become ordinary knobs, its reported latency feeds delay compensation, and its state is saved in the project — but its own editor window is never shown.

A hosted plug-in is subject to the same real-time rule as everything in the engine: the audio thread may not allocate, lock, block, log or do I/O. That is why the out-of-process bridge (ADR-0033) hands block N over and plays block N-1's answer instead of waiting.

The decision records

An ADR records why something was decided: the situation, the options considered, the decision, and the consequences. New significant decisions get a new numbered file here; the architectural changelog (CHANGELOG-ARCH) links each change to its ADR.

# Title Status
0001 Technology stack (with Qt-version and owner-decision amendments) Accepted
0002 Project licence, MIT SDK split, dependency policy and list Accepted
0003 Threading model and real-time rules Accepted (graph swap details in Phase 2)
0004 Command system and undo/redo Accepted, implemented (Phase 3, slice 1)
0005 File formats (preset, patch, project v1 implemented; bundle/journal later) Accepted for preset/patch/project; Proposed for the rest
0006 Plugin sandbox protocol Planned (Phase 2 spike / 7)
0007 Device and parameter model (C ABI proposed) Accepted in-process; C ABI Proposed
0008 UI architecture (Qt Quick, themes, help strip) Accepted
0009 32-bit bridge design Dropped from critical path (owner decision)
0010 Graph Builder runtime and plugin export Planned (Phase 12)
0011 Audio backends and ASIO Accepted
0012 VST2 stance Decided: no VST2 (see owner decisions)
0013 Tempo sync (Ableton Link) Decided: not implemented (owner decision)
0014 Video codecs and FFmpeg build Planned (Phase 14)
0015 ML models and licences Planned (Phase 13)
0016 Warp modes and time-stretch Planned (Phase 5)
0017 Audio graph, render plan and offline-first engine Accepted (slices 1-4 implemented)
0018 Modular synthesis, numbered input slots and patches Accepted
0020 Actions, shortcuts and keymaps (ActionRegistry, contexts, keymap.json, .nebconfig) Accepted, implemented (Phase 3)
0019 Nebula devices as standalone plug-ins (CLAP/VST3/LV2 export, licence of the plug-in line) Planned (post-1.0; foundations kept in mind from now on)
0021 Musical time and TempoMap (ticks, piecewise tempo/time-signature, bar/beat/tick) Accepted, implemented
0022 The project-to-audio bridge (services, ProjectRenderer, VoiceAllocator) Accepted, implemented
0023 The five-rule architecture charter (thread separation, lock-free comms, instrument API, Closed Core/Open API, domain folders) + the migration plan Accepted — docs/architecture/README.md is the source of truth
0024 The Control API v1: local TCP JSON-RPC server, token auth, first method set (api_server/ + app/src/api_controller.*) Accepted, implemented
0025 FAUST as a build-time-only code generator for instruments (plan P5) Accepted
0026 Structural headroom: equal-energy polyphony (√voices), a keyboard velocity curve, and a shared transparent-by-default master limiter Accepted, implemented
0027 MIDI output: the song's notes and clock go out of a port Accepted
0028 A multi-core render scheduler: render independent tracks at once, sum in one fixed order Accepted
0029 An MCP server, as a client of the Control API Accepted
0030 Clock receive, MIDI Timecode and program change (following external gear; the per-track MIDI channel) Accepted
0031 Lua controller scripts (a vendored, sandboxed Lua host) and MPE (per-note expression, in and out) Accepted
0032 Hosting CLAP plug-ins (and why not VST3) Accepted, implemented (engine/plugins side; see its "Not built" list)
0033 The out-of-process plugin bridge (hosting a plugin in a process of its own; 32-bit or foreign-licence plugins) Accepted, implemented (protocol, host, bridge process, stub plugin; a 32-bit bridge is not built here)
0034 Per-track MIDI output ports, control-change (CC 7 / CC 10) automation out, and a configurable MIDI-out drain interval Accepted, implemented

Settings design is documented in architecture/settings.md.

Updated 2026-11-03

An unhandled error has occurred. Reload 🗙

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.