diff --git a/docs/faust-toolchain-boundary.md b/docs/faust-toolchain-boundary.md
index 2a717a1..5027d99 100644
--- a/docs/faust-toolchain-boundary.md
+++ b/docs/faust-toolchain-boundary.md
@@ -91,6 +91,25 @@ search; `FaustHome` then supplies `share/faust` if the prefix is not the file's
grandparent. The loader prepends `lib` and `bin` to `PATH` using the platform
path separator.
+## Linux Native Toolchain
+
+Built and run on Debian 13 (2026-11-03). Faust `aa30fd7` (2.90.20), LLVM 19
+(`LLVM_CONFIG=/usr/lib/llvm-19/bin/llvm-config`, `INCLUDE_DYNAMIC=ON`,
+`backends/most.cmake`), installed to a prefix; `make -j4`. The first link
+failed with `cannot find -lPolly -lPollyISL -lzstd`; the install prefix that
+resulted has `lib/libfaust.so` but `share/faust` holds only the architecture
+files, not `stdfaust.lib` and the other `.lib` files (they live in the separate
+faustlibraries repo). Compiling any patch that imports `stdfaust.lib` then
+fails with `unable to open file stdfaust.lib`. Point `AQUASYNTH_FAUST_HOME` at a
+home whose `lib/` has `libfaust.so` and whose `share/faust` has both.
+
+`tools/FaustRender` renders one `.dsp` with an `rpm` control to WAVs plus
+`manifest.json` (hashes, peak, rms, non-finite flag). It measures; it does not
+listen. `docs/renders/thruster-v5-manifest.json` is its output for
+`patches/thruster/thruster.dsp` at PR #3 head `c6561da`, 4 s per case. The render is
+deterministic (two reruns byte-identical); the 2026-10-31 sweep file is a 6 s
+render, which is why its hash differs from a 4 s sweep.
+
## Invariants
- AquaSynth remains the authority for what a patch means.
diff --git a/docs/renders/thruster-v5-manifest.json b/docs/renders/thruster-v5-manifest.json
new file mode 100644
index 0000000..1907dea
--- /dev/null
+++ b/docs/renders/thruster-v5-manifest.json
@@ -0,0 +1,46 @@
+{
+ "patch": "thruster",
+ "dspSha256": "8afc045eab94f396e21d96a262c5d71910103acd51da5913d3eb3036b48a5809",
+ "sampleRate": 48000,
+ "blockFrames": 512,
+ "seconds": 4,
+ "rpmControl": "rpm",
+ "os": "Debian GNU/Linux 13 (trixie)",
+ "faustHome": "/var/lib/mind-aqua-self/faust-home-overlay-20261031",
+ "faustLibrary": "/var/lib/mind-aqua-self/faust-home-overlay-20261031/lib/libfaust.so",
+ "cases": [
+ {
+ "label": "low-rpm600",
+ "file": "thruster-low-rpm600.wav",
+ "rpmStart": 600,
+ "rpmEnd": 600,
+ "frames": 192000,
+ "peak": 0.18832043,
+ "rms": 0.07348131804614891,
+ "nonFinite": false,
+ "sha256": "4aeaa1f4d9aa5d63025dd8b3d45b25fc6b030a366ba05237c0021b508308e0ba"
+ },
+ {
+ "label": "high-rpm4800",
+ "file": "thruster-high-rpm4800.wav",
+ "rpmStart": 4800,
+ "rpmEnd": 4800,
+ "frames": 192000,
+ "peak": 0.3635283,
+ "rms": 0.14323418081979195,
+ "nonFinite": false,
+ "sha256": "390874aa1d5c4d50f251fa0735b82f859bba43ffee4236fd24d5b06a4733ef7d"
+ },
+ {
+ "label": "sweep-600-4800",
+ "file": "thruster-sweep-600-4800.wav",
+ "rpmStart": 600,
+ "rpmEnd": 4800,
+ "frames": 192000,
+ "peak": 0.34473976,
+ "rms": 0.11363349353245127,
+ "nonFinite": false,
+ "sha256": "ad8d1508aa2b37b5038a6acfcfb21fc46ae04f0180adcd6ea60edc7e918e4068"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tools/FaustRender/FaustRender.csproj b/tools/FaustRender/FaustRender.csproj
new file mode 100644
index 0000000..9dd3508
--- /dev/null
+++ b/tools/FaustRender/FaustRender.csproj
@@ -0,0 +1,12 @@
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ true
+
+
+
+
+
diff --git a/tools/FaustRender/Program.cs b/tools/FaustRender/Program.cs
new file mode 100644
index 0000000..1f0861b
--- /dev/null
+++ b/tools/FaustRender/Program.cs
@@ -0,0 +1,90 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Text.Json;
+using AquaSynth.Faust;
+
+// Offline probe: render one Faust source with a swept `rpm` control to mono 16-bit WAVs through the
+// native libfaust path, and write manifest.json (hashes, levels, toolchain). It measures peak, rms and
+// NaN only; it cannot say how anything sounds.
+// usage: FaustRender