1
0
Fork 0
forked from hanez/xtc

Phase 9: Add native processors and reproducible Linux release tooling

This commit is contained in:
Johannes Findeisen 2026-10-27 01:21:04 +01:00
commit de403b37d7
43 changed files with 1656 additions and 19 deletions

View file

@ -130,3 +130,14 @@ stop transport before subsequent preparation. `src/project/history.c` provides
cursor only after successful GUI/backend installation. No samples or FLTK state
are stored in history. `mixer_widget.cpp` is only controls/drawing/gestures.
See MIXER.md and PROJECT_FORMAT.md for ownership, limits and v4 migration.
## Phase 9 native lifecycle and distribution
`xtc_native.h` defines copied versioned C descriptors and bounded block contexts.
Native effect/source nodes prepare independent instances on control-thread graph
compilation and reclaim them only on compile failure, quiescent destruction or
retirement collection. Event instruments can render into the public graph source
buffer. No descriptor/state pointers enter project files; unsupported native
saves preserve files. See NATIVE_API.md and decision 0008. Release tooling lives
outside the runtime, with source manifests, deterministic exports and staged
installation/consumer checks. See RELEASING.md for reproducibility scope.

View file

@ -263,3 +263,13 @@ and publishes independently atomic output peak snapshots. Callback work remains
bounded and allocation/I/O/lock free. Meter UI reads no active plan memory.
See MIXER.md for exact pan law, automation interpolation, event/source exceptions,
memory/history bounds and verification. Input/recording remain mono.
## Phase 9 native processing
Graph-native effects/sources use copied versioned descriptors, two independently
prepared mono instances and the existing plan ownership/retirement protocol.
Reset/process callbacks must be realtime-safe; prepare/destroy never run in
processing. Callback errors silence the block and reset the failing processor,
without rolling back already advanced DSP. Native event instruments can render
explicitly into graph instrument buffers. See NATIVE_API.md for exact lifecycle
and serialization limits and PERFORMANCE.md for measured workload and budgets.

View file

@ -196,3 +196,29 @@ ASan/UBSan core/GUI suite passed without compiler warnings. GUI leak checking
retains only the existing fontconfig suppression. ThreadSanitizer also passed
the recorder concurrency and realtime publication tests. Physical devices and
long-running stereo performance remain unverified.
## Phase 9 release preparation
```sh
make all test gui debug test-gui test-gui-jack test-jack test-jack-audio test-jack-midi
make test-install test-recovery test-performance
make test-release
```
Core test now includes native ABI/lifecycle/retirement and the native-gain example.
Optional release tests use Python 3.12+ standard library, Git for indexed source
exports, GNU/LLVM deterministic ar and temporary directories. No live prefix is
installed. test-install checks core/GUI files, a prefix containing spaces,
headless CLI, pkg-config C/C++ consumers, exact/idempotent uninstall and preservation
of unrelated files. test-release compares two clean exported optimized builds and
repacked archives byte-for-byte. test-recovery kills private child writers and
forces a child-local write limit, checking complete finalized destinations and
explicit orphan temporaries. Performance timings are reported, not used as a
universal realtime guarantee. See RELEASING.md, RECOVERY.md and PERFORMANCE.md.
Phase 9 verification passed a fresh warning-enabled core/debug/GUI build, all
software JACK targets, staged install/uninstall, recovery and performance tests.
No-JACK core/GUI/install checks and Clang ASan/UBSan core/GUI checks also passed
(the GUI retains only its existing fontconfig leak suppression). TSan recorder
and realtime-handoff checks passed. Two independent optimized source exports
and their repacked source archives were byte-identical on this toolchain.

39
docs/LICENSES.md Normal file
View file

@ -0,0 +1,39 @@
# Licensing and dependency review
The repository's original code, example and documentation are under the MIT
license in LICENSE (copyright Johannes Findeisen). No energyXT source, artwork,
external plugin SDK, vendored DSP or binary asset is included. The source archive
contains repository files and an export manifest; it does not bundle system
libraries. Installation includes LICENSE and these documents.
The Phase 9 host review inspected installed package metadata and local license
files. Package-level license sets can include utilities/docs that differ from the
specific library being linked; retain the relevant upstream notices when
redistributing dependencies. The default install uses system shared libraries.
| Component | Inspected license / role |
| --- | --- |
| FLTK 1.4.5 | LGPL-2.0-only with FLTK exception; optional GUI. `/usr/share/licenses/fltk/COPYING` contains the linking/subclass exceptions. No local modifications. |
| PipeWire JACK client library | Installed client COPYING is MIT (Wim Taymans); package metadata also lists GPL/LGPL components. No PipeWire binaries are bundled. |
| Traditional JACK client | Alternative backend library commonly LGPL; inspect the actual distribution package instead of assuming PipeWire's license. |
| glibc/libm | LGPL-2.1-or-later library code; package also contains GPL components. System runtime. |
| libstdc++/libgcc | GPL-3.0-or-later with GCC Runtime Library Exception 3.1. System C++ runtime. |
| Pango/GLib | LGPL family; installed Pango metadata LGPL-2.0-or-later. FLTK text stack. |
| Cairo | LGPL-2.1-only OR MPL-1.1. FLTK graphics stack. |
| Fontconfig | HPND AND Unicode-DFS-2016 in inspected package. |
| FreeType | FTL OR GPL-2.0-or-later; distribution can select FTL. |
| HarfBuzz, Wayland, libdecor, xkbcommon | MIT-family licenses; check each shipped package. |
| X11 libraries | MIT/X11-family notices. |
| D-Bus | AFL-2.1 OR GPL-2.0-or-later. |
| Python 3 | PSF-2.0; optional build/test/package scripts use only the standard library. Not an application runtime dependency. |
GNU Make, GCC/Clang, binutils, pkg-config and optional Xvfb/xdotool/sanitizers are
build/test tools, not shipped dependencies. New Python tooling needs Python 3.12+
for safe tar extraction in release tests; this host has Python 3.14.8. No packages
were installed and no application dependency was added in Phase 9.
Binary redistributors must inspect their own `readelf -d`/`ldd` dependency set
and supply the notices/source/relinking facilities required by those exact
licenses and linking choices. FLTK's exception does not remove obligations of
other linked libraries. This review covers the current unmodified system-linked
build; static all-in-one bundles and external plugins need a separate review.

58
docs/LINUX.md Normal file
View file

@ -0,0 +1,58 @@
# Linux setup and installation
Use a C99 compiler, C++17 compiler for the GUI, GNU Make, binutils and pkg-config.
The core needs only the C/math runtime. FLTK 1.4 development files enable the GUI;
JACK-compatible development files enable the optional audio/MIDI backend.
Packaging/install tests additionally use Python 3.12+ (standard library only).
No command starts a server, installs dependencies or changes system settings.
```sh
make all test
make gui
./build/xtc
```
Start/configure a JACK or PipeWire JACK-compatible server through your existing
system setup first. xtc opens without starting one and requires reconnect after
server loss/rate changes. In a separate JACK routing tool connect desired MIDI
to `midi_in`, audio capture to `input`, and `output`/`output_right` to left/right
playback ports. No physical routes are automatic. Input/recording remain mono.
See GUI.md, AUDIO_FILES.md, MIDI.md and MIXER.md for composing/recording/editing.
`xtc --help` and `xtc --version` work without a display/server. `xtc project.xtc`
opens one project; no-file startup retains FLTK options such as `-display`.
`xtc-check --validate project.xtc` validates metadata only; native graphs cannot
be serialized. See RECOVERY.md before replacing damaged files.
## Staged installation
Core-only installation retains the original `make install` behavior and now
adds xtc-check, pkg-config metadata, license and documentation. install-gui adds
the GUI executable and desktop launcher. Defaults to PREFIX=/usr/local:
```sh
make install-gui DESTDIR="$PWD/build/stage" PREFIX=/usr
make uninstall DESTDIR="$PWD/build/stage" PREFIX=/usr
```
Review staged contents before using a package manager or choosing a writable
live prefix. For a user installation, use an absolute PREFIX such as
`$HOME/.local` and ensure its bin is in PATH. Prefixes containing spaces are
tested; shell metacharacters/newlines, quotes, dollar signs and backslashes are
not supported. No root installation is performed by tests. Uninstall from the
same source version and prefix: it removes only known files, keeps unrelated
files and removes directories only if empty. It is idempotent. It is not a
system package manager and cannot undo replacement of another installation.
Installed files: bin/xtc-check, optional bin/xtc, include/xtc/*.h, lib/libxtc.a,
lib/pkgconfig/xtc.pc, share/doc/xtc, share/licenses/xtc/LICENSE and optional
share/applications/xtc.desktop. No daemon, service, bundled library or MIME
registration is installed. Desktop caches are left to the packager/environment.
The launcher uses the system audio icon and can open a single file argument.
C/C++ consumers use `pkg-config --cflags --libs --static xtc` plus the chosen
compiler; expose PREFIX/lib/pkgconfig through PKG_CONFIG_PATH when needed.
Static backend consumers need JACK if that optional backend was built; the pc
file records its private dependency. Public headers contain no JACK/FLTK types.
To build without optional libraries, set PKG_CONFIG=false FLTK_CONFIG=false.
Python is not needed to run the installed executables or library.

83
docs/NATIVE_API.md Normal file
View file

@ -0,0 +1,83 @@
# Native C processor API, version 1
`xtc_native.h` defines a small compile-time extension boundary for trusted C99
sources, effects and instruments. Build `make build/native-gain` and run it for a
complete graph-effect example in `examples/native_gain.c`. This is a source API
for this release, not a promise of binary compatibility across builds/platforms.
There is no module scanner, dynamic loader, SDK dependency or sandbox.
## Descriptor and ownership
Set `abi_version = XTC_NATIVE_ABI_VERSION`, `struct_size = sizeof(descriptor)`,
a terminated printable non-space ASCII identifier (1..63 bytes), kind and all
four callbacks. Unknown ABI/size returns UNSUPPORTED; invalid fields return
INVALID_ARGUMENT. The host copies the entire descriptor, including its name.
Callback code must remain loaded until all graphs, plans and instances are gone.
No plugin/global registry is consulted or mutated by rendering.
`xtc_native_create` validates rate (positive, <=768000), frames (1..65536) and a
finite scalar parameter. It runs prepare on the control thread with a NULL
initial state pointer. Prepare may allocate/preload; on failure the host calls
destroy even for NULL state. Destroy must accept NULL. The host then resets the
new instance. On failure the output handle is NULL; successful instances belong
to the caller. Destroy only after the processing owner is stopped/joined.
Reset and process have one audio owner (or run while stopped). They must never
allocate/free, perform I/O/logging, block, call GUI functions or mutate shared
control data. Reset must be idempotent. Prepare/destroy run only on the control
thread, including failed compilation and retired-plan collection. Code executed
inside callbacks is trusted: xtc cannot enforce its realtime behavior or protect
against memory corruption. C++ processors must use C linkage and never throw
across this boundary; domain/DSP code in this repository remains C.
## Block contract
A block contains mono input/output, frames, absolute sample position and optional
MIDI events. Input NULL means silence; exact input/output aliasing is allowed and
processors must support it. Output is writable for every frame, including silent
frames; do not retain any borrowed block pointers. Other overlapping buffers are
not supported. Only instruments receive events; event count <=256, offsets are
sorted and strictly less than frames, with equal offsets retaining caller order.
Pitch/velocity/channel/type bounds follow xtc_midi.h. Zero frames is a no-op.
The host rejects invalid blocks before changing state/output, and reports
CAPACITY above the prepared block limit. Position arithmetic cannot overflow.
A discontinuous position resets held voices/state before processing. Call reset
for Stop/panic when hosting instruments yourself; send channel ALL_OFF for
channel-specific panic. The native host does not implement sustain or routing.
On successful processing, nonfinite samples become silence. Callback failure
silences the block, resets that processor and returns its error. Already advanced
processors in a graph are not rolled back. Never rely on failure to transactionally
rewind DSP; prepare fallible resources before publication. There is no latency
compensation, tail protocol, parameter ramp or dynamic port negotiation in ABI 1.
## Graph and instrument integration
`xtc_graph_add_native` adds effects (input 0/output 1) or sources (output 1), with
a scalar immutable parameter. Compilation creates two independent mono instances
for stereo; no mutable state is shared between sides. Graph-only edits retain
the existing pending/retired publication contract. Reset clears native state and
its graph-local frame clock; graph positions count successfully rendered frames
since compilation/reset, independently of backend seek/transport. Sources needing
transport time should be hosted explicitly with a block position.
For an instrument, use create/process directly with sample-offset events, then
feed its output to an existing XTC_NODE_INSTRUMENT through process_sources or
process_stereo. The host owns event routing and panic/reset delivery. Graph-native
instrument addition returns UNSUPPORTED because current graph ports carry audio,
not MIDI. This separation avoids silently dropping instrument events. The built-in
JACK MIDI sine instrument remains the GUI's instrument; custom instrument selection
and event graph ports are future work.
Descriptors survive graph/project cloning and transactional remove/connect edits;
custom ADD_NODE commands use the dedicated constructor rather than a GUI menu.
Native nodes have no project-file representation: Save returns UNSUPPORTED and
preserves the destination. Format v4 still accepts only builtin types 1..6.
A durable identifier/registry, versioned processor state and missing-module policy
must precede serialized native extensions or external plugin hosting.
Tests cover ABI/size/field rejection, descriptor copying, aliased buffers,
prepare/process failures, stereo compile cleanup, retirement/destruction outside
processing, event offsets, seek note reset, source ports, instrument-buffer graph
routing and unsupported-save preservation. Run `make test`.

35
docs/PERFORMANCE.md Normal file
View file

@ -0,0 +1,35 @@
# Performance and realtime qualification
`make test-performance` runs a hardware-independent 48 kHz, 128-frame stereo
workload: 64 graph nodes, 4096 MIDI notes (32 simultaneous), 16 tracks and gain
automation. Preparation and block timing use CLOCK_MONOTONIC outside the engine;
callback code itself does not clock or log. The test checks finite output and
reports mean, p99, maximum and CPU-time/audio-duration ratio. A run is a workload
measurement, not a universal realtime threshold. Run more blocks with:
```sh
./build/test_performance 10000
```
Initial Phase 9 measurement on an AMD Ryzen Threadripper 1920X, GCC 16.2.1, -O2,
2000 blocks: preparation 3.045 ms, mean 308.301 us, p99 422.153 us, maximum
665.096 us, versus a 2666.667 us block deadline; CPU/audio ratio 0.1156. These are
one host/run's observed values and depend on scheduling/load/compiler. The test
excludes WAV disk preparation, external/native plugin complexity, GUI redraw,
server scheduling and actual hardware latency. Native callbacks must provide
their own bounded realtime implementation.
Existing software JACK loopback, stereo MIDI/save/reopen, record/reopen,
restart/close and concurrent graph handoff tests remain available. Runtime
fault injection covers loss/rate/xrun/null-buffer and lifecycle failures without
altering the shared server. ASan/UBSan validate memory/error paths; TSan checks
recorder/handoff races, not timing. Sanitized runs are not latency qualification.
Current worst-case storage includes up to 256 MiB preloaded mono WAV samples,
paired graph buffers (128 MiB at 256 nodes/65536 frames; GUI limit 32 MiB), bounded
MIDI/mix metadata and up to 33 metadata/graph history snapshots. Pending/retired
plans can retain additional graph memory. Native preparation can allocate more;
the API cannot enforce an extension's memory budget. There is no CPU overload
scheduler, disk streaming, latency compensation or callback allocator audit of
arbitrary third-party code. High-density interactive projects, physical devices,
long-duration stereo/recording and live server termination remain unqualified.

24
docs/PLUGIN_HOSTING.md Normal file
View file

@ -0,0 +1,24 @@
# External plugin hosting evaluation
Decision: defer external hosting after Phase 9. The native source API is useful
for proving processor contracts without adding an SDK or scanning/loading code.
| Candidate | Benefits | Work required before adoption |
| --- | --- | --- |
| LV2 | Open specifications, established Linux hosts/plugins | RDF discovery and feature negotiation, URIDs, state, UI lifetime, worker requests and event mapping; likely additional discovery dependency |
| CLAP | C ABI, explicit lifecycle and event model | Extension negotiation, parameter/state persistence, host callbacks, main/audio-thread scheduling and UI integration |
| VST3 | Broad plugin availability | SDK/license review for the selected SDK release, C++ adapter outside the C core, component/controller and editor lifecycle, state and event translation |
No candidate was linked, scanned or loaded in this phase. API/SDK and plugin
licenses must be checked at the revision actually adopted; they are separate
from xtc's MIT license. Supporting a format does not grant redistribution rights
to individual plugins. Do not search user plugin directories from the callback.
Before implementation, require versioned module identity and state, a missing-
plugin recovery policy, preparation failure/partial cleanup tests, bounded events
and parameter publication, latency/tail handling, callback-safe unload after
retirement, a GUI/main-thread boundary and an explicit crash-containment decision.
Native ABI tests cover some ownership mechanics but do not prove third-party
code safe. Event graph ports and custom-instrument GUI selection remain gaps.
Choose one format with concrete user needs after those contracts are specified;
do not introduce a general multi-format framework now. See decision 0008.

View file

@ -105,3 +105,12 @@ external absolute paths, not copied on Save As. Metadata loading preserves
missing references; audio-plan preparation reports unreadable/invalid assets
and renders them silently. Relinking replaces a selected clip's path without
changing its IDs or arrangement. See AUDIO_FILES.md for recovery and limitations.
## Phase 9 extension and recovery policy
Format remains v4. Native processor descriptors are process-local and have no
serialized representation; saving a native graph returns UNSUPPORTED and retains
the existing destination. Unknown file versions/types remain rejected. See
NATIVE_API.md for the extension boundary and RECOVERY.md for write-failure, killed
writer and unfinished-recording behavior. Metadata validation is available via
`xtc-check --validate PROJECT` without a GUI or server; it does not load assets.

48
docs/RECOVERY.md Normal file
View file

@ -0,0 +1,48 @@
# Persistence and failure recovery
The project format remains v4. Readers migrate v1/v2/v3, validate bounded input
and atomically install a candidate only after complete success. Existing tests
cover exact migration/roundtrip, truncations, malformed references, counts,
IDs, nonfinite values and failed GUI/backend installation. Native processors
are session-only; saving their graph returns UNSUPPORTED rather than writing
process addresses or an unreadable project. See PROJECT_FORMAT.md.
Project Save writes a mode-0600 same-directory `NAME.tmp.XXXXXX`, flushes and
fsyncs it, closes and renames it over the destination. Before rename, handled
failures remove the temporary and retain the destination. POSIX rename gives
complete old-or-new destination contents during process failure. The directory
is not fsynced, so this is not a power-loss durability guarantee. Symlinks are
replaced rather than followed at rename; original file permissions are not kept.
Recording similarly writes/finalizes a same-directory temporary, then renames
it. Stop must synchronize callbacks before draining/finalizing. Ring overflow
returns an explicit error and may finalize a partial take for manual recovery;
I/O failure retains the previous destination. A killed recorder does not finish
its WAV header. Source WAV files remain external absolute paths; Save As does
not collect them. Back up the project and assets together.
## After a crash
1. Preserve the destination and any `.tmp.*` files before attempting repair.
2. Run `xtc-check --validate project.xtc` for metadata validation without a GUI,
display or audio server. This does not verify assets or realtime preparation.
3. A valid destination can be opened normally. If only a temporary contains useful
data, copy it to a separate candidate and validate that copy before replacing
anything. Partial project files are rejected; there is no salvage parser.
4. An unfinished WAV temporary may have a zero/incorrect data length and needs
a separate recovery tool or manual header repair on a copy. xtc does not guess
its intended length or automatically rename orphan temporaries.
5. Missing assets preserve silent clips. Use Relink selected audio, then Play to
revalidate. For server loss/format changes, Stop/Reconnect and explicit routing
are required. Panic/F9 clears held notes. Failed close retains ownership for retry.
There is no autosave, crash journal, automatic orphan recovery or persisted Undo
history. Existing finalized files are protected against the tested process-level
failures; unsaved edits/in-flight takes can still be lost.
`make test-recovery` uses temporary directories and child processes only. It
forces an EFBIG-style write failure with a child-local file-size limit, kills
eight project writers while temporaries are visible, and kills a recorder after
draining but before finalization. Complete old/new destinations and explicit
orphan temporaries are verified. It does not cut power, terminate the shared
audio server, simulate a failing filesystem, or qualify disk/device endurance.

56
docs/RELEASING.md Normal file
View file

@ -0,0 +1,56 @@
# Release preparation (0.1.0 development prototype)
Phase 9 makes source packaging, staged install/uninstall and repeatable builds
testable. It does not turn the prototype into a production-qualified DAW. The
version remains 0.1.0; no tag, external release or binary publication is created
by `make dist` or `make release`.
## Build and package
```sh
make release
make test-install
make test-recovery test-performance
make dist
make test-release
```
release builds optimized, warning-enabled C/C++ code, tests and the optional GUI
in BUILD_DIR/release with no debug paths, a source-prefix map, deterministic ar
metadata and FLTK include/define flags rather than distribution-specific LTO
flags. It retains assertions so the test harness actually runs. dist creates
BUILD_DIR/xtc-0.1.0.tar.gz using Python's standard library. It includes only Git
indexed regular files (working-tree contents, including staged new files), plus
a source manifest; stage intended files first and check `git diff --cached`.
Untracked files, ignored build output and .git are excluded. Exported archives
can reproduce themselves without Git using SOURCE_MANIFEST.json.
Archive order, permissions, ownership, gzip filename and timestamps are normalized.
SOURCE_DATE_EPOCH defaults to 0; set an unsigned 32-bit epoch explicitly if desired.
The archive does not claim to identify a commit: release from a clean checkout
and record its commit/hash externally. Do not treat a dirty-tree archive as an
attested release. No artifact is uploaded by these targets.
test-release unpacks the source into two different temporary paths, runs release
in each, compares libxtc.a/xtc-check/optional xtc SHA-256 values, and repacks each
export to compare archive bytes. This verifies reproducibility for the same host,
compiler, flags and dependency versions. It does not promise bit-identical output
across distributions, compilers or library updates. Compiler/debug/sanitizer
build directories are intentionally separate; changing toolchain/flags in place
requires a clean directory. Python release tests require version 3.12+.
## Qualification record
Run normal core/GUI and live software JACK tests, no-JACK builds and ASan/UBSan.
GUI leak tests retain the narrow fontconfig suppression; core tests do not.
TSan recorder/publication tests complement lifecycle tests. Native API tests
verify callback failures, cleanup and retirement; they cannot certify arbitrary
extensions. See BUILD.md and tests/integration/README.md for commands.
Record versions, archive hashes, installation/consumer checks and measured timing.
Use LICENSES.md for dependency/license review and PLUGIN_HOSTING.md for the
external-hosting decision. Review RECOVERY.md for migration and process-crash
semantics. Physical devices, power loss, sustained recording/performance, user
acceptance on other distributions and binary redistribution are still separate
qualification work. There is no DEB/RPM/AppImage, bundled runtime, autosave,
external plugin host or stable shared-library extension ABI in this release.

View file

@ -105,4 +105,15 @@ implemented composition/mixing workflow.
Native node API, evaluate plugin hosting, licensing review, packaging,
installation tests, performance and recovery tests.
Implemented: versioned native C processor lifecycle and graph source/effect API,
explicit instrument-buffer integration, example and error/retirement tests;
external-hosting evaluation (deferred); dependency/license review; deterministic
source/release targets; core/GUI staged install/uninstall and installed C/C++
consumer tests; headless version/help/metadata validation; offline performance
and process-crash/write-failure recovery tests. Source format stays v4, reading
v1–v3. See NATIVE_API.md, RELEASING.md, LINUX.md, LICENSES.md, PERFORMANCE.md and
RECOVERY.md. Native graphs remain session-only, with unsupported saves preserving
files. No stable binary plugin ABI, external host, system package, autosave or
physical-device/endurance qualification is claimed.
Do not skip phases without documenting why and preserving acceptance criteria.

View file

@ -0,0 +1,19 @@
# 0008 — Native processors and source release preparation
Status: accepted, Phase 9.
Add a versioned copied C descriptor with control-thread prepare/destroy and
single-owner realtime reset/process. Prove effects/sources in the existing graph,
with two independent mono instances for stereo, and event instruments rendered
through the public instrument-source buffer boundary. Keep trusted code statically
linked. Native project serialization and graph event ports need more contracts;
reject unsupported saves without replacing files. Defer LV2/CLAP/VST3 after a
concrete evaluation rather than linking SDKs speculatively.
Keep format v4 and test migration plus process-crash/write-failure behavior.
Provide deterministic source exports and optimized release builds, staged
core/GUI installation, exact uninstall and installed C/C++ consumer tests. Use
Python standard-library tools only for packaging/testing; no application runtime
dependency or system changes. Document dependency licenses, Linux routing and
measured offline performance separately from unverified hardware/endurance.
See NATIVE_API.md, PLUGIN_HOSTING.md, RELEASING.md and RECOVERY.md.