<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://rehsack.de/feed.xml" rel="self" type="application/atom+xml" /><link href="https://rehsack.de/" rel="alternate" type="text/html" /><updated>2026-04-15T20:53:10+00:00</updated><id>https://rehsack.de/feed.xml</id><title type="html">Jens Rehsack</title><subtitle>Persönlicher Blog von Jens Rehsack — Technik, Meinung, Leben</subtitle><author><name>Jens Rehsack</name></author><entry><title type="html">Validate-First: AsciiDoc-Projekte mit Devcontainer und docker-compose</title><link href="https://rehsack.de/2026/04/12/validate-first-docker-compose-devcontainer/" rel="alternate" type="text/html" title="Validate-First: AsciiDoc-Projekte mit Devcontainer und docker-compose" /><published>2026-04-12T00:00:00+00:00</published><updated>2026-04-12T00:00:00+00:00</updated><id>https://rehsack.de/2026/04/12/validate-first-docker-compose-devcontainer</id><content type="html" xml:base="https://rehsack.de/2026/04/12/validate-first-docker-compose-devcontainer/"><![CDATA[<div class="paragraph">
<p>Im <a href="/2026/04/05/devcontainer-asciidoc-jekyll/">letzten Post</a> ging es
um einen einzelnen Container für Jekyll + AsciiDoc.  Diesmal wird es
spannender: zwei Container, die zusammenarbeiten&#8201;&#8212;&#8201;einer validiert,
der andere baut.</p>
</div>
<div class="sect1">
<h2 id="das-problem-doctoolchain-schluckt-fehler">Das Problem: doctoolchain schluckt Fehler</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://doctoolchain.org/">docToolchain</a> ist das Referenz-Werkzeug für
arc42/req42-Dokumentation.  Es baut HTML und PDF aus AsciiDoc-Quellen&#8201;&#8212;&#8201;zuverlässig, erprobt, gut integriert.</p>
</div>
<div class="paragraph">
<p>Was es nicht tut: bei kaputtem AsciiDoc abbrechen.  Fehlende Includes,
aufgelöste Cross-Referenzen, Syntaxfehler&#8201;&#8212;&#8201;docToolchain meldet sie als
Warnung und baut trotzdem weiter.  Am Ende hat man ein PDF mit
Platzhaltern, wo eigentlich Inhalte stehen sollten.</p>
</div>
<div class="paragraph">
<p>In einer CI-Pipeline ist das fatal.  Der Build ist „grün", das Artefakt
ist kaputt.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="die-lösung-validierung-als-gate">Die Lösung: Validierung als Gate</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Der <code>ghcr.io/tpo42/adoc</code>-Container (der aus dem
<a href="/2026/04/05/devcontainer-asciidoc-jekyll/">Jekyll-Post</a> bekannte)
enthält nicht nur die AsciiDoc-Toolchain, sondern auch ein
<code>validate</code>-Kommando.  Dahinter steckt ein <code>asciidoctor</code>-Aufruf im
Strict-Modus: fehlende Dateien, kaputte Referenzen, Syntaxfehler&#8201;&#8212;&#8201;alles wird zum Fehler, nicht zur Warnung.</p>
</div>
<div class="paragraph">
<p>Die Idee: Validierung <em>vor</em> dem Build.  Erst wenn die Quellen sauber
sind, darf docToolchain bauen.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="zwei-container-ein-workflow">Zwei Container, ein Workflow</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Statt alles in einen Container zu packen, trennen wir die Concerns:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 40%;">
<col style="width: 40%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Container</th>
<th class="tableblock halign-left valign-top">Aufgabe</th>
<th class="tableblock halign-left valign-top">Image</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>adoc</strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AsciiDoc-Validierung, Toolchain für Entwicklung</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ghcr.io/tpo42/adoc:latest</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>doctoolchain</strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PDF- und HTML-Build</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>doctoolchain/doctoolchain:v3.4.2</code></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Das Bindeglied ist eine <code>docker-compose.yml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="na">services</span><span class="pi">:</span>
  <span class="na">adoc</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">ghcr.io/tpo42/adoc:latest</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">.:/workspace</span>
    <span class="na">command</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">sleep"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">infinity"</span><span class="pi">]</span>

  <span class="na">doctoolchain</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">doctoolchain/doctoolchain:v3.4.2</span>
    <span class="na">platform</span><span class="pi">:</span> <span class="s">linux/amd64</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">.:/project</span>
      <span class="pi">-</span> <span class="s">dtc-gradle-cache:/home/dtcuser/.gradle</span>
    <span class="na">entrypoint</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">sleep"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">infinity"</span><span class="pi">]</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">DTC_HEADLESS</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
      <span class="na">DTC_OPTS</span><span class="pi">:</span> <span class="pi">&gt;-</span>
        <span class="s">-PmainConfigFile=docToolchainConfig.groovy</span>
        <span class="s">--warning-mode=none -Dfile.encoding=UTF-8</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">dtc-gradle-cache</span><span class="pi">:</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Beide Container laufen dauerhaft (<code>sleep infinity</code>).  Befehle gehen per
<code>docker compose exec</code> rein&#8201;&#8212;&#8201;kein Container-Start-Overhead pro Aufruf.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="der-devcontainer">Der Devcontainer</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Die <code>.devcontainer/devcontainer.json</code> referenziert dieselbe Compose-Datei:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="json"><span class="p">{</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"mein-doku-projekt"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"dockerComposeFile"</span><span class="p">:</span><span class="w"> </span><span class="s2">"../docker-compose.yml"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"service"</span><span class="p">:</span><span class="w"> </span><span class="s2">"adoc"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"workspaceFolder"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/workspace"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"shutdownAction"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stopCompose"</span><span class="w">
</span><span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>devcontainer up</code> startet <em>beide</em> Container.  Der adoc-Container ist
der Primary&#8201;&#8212;&#8201;dort editiert und validiert man.  Der
doctoolchain-Container läuft als Sidecar für Builds.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="der-workflow">Der Workflow</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Container starten</span>
docker compose up <span class="nt">-d</span>

<span class="c"># Validieren</span>
docker compose <span class="nb">exec </span>adoc validate <span class="se">\</span>
    <span class="nt">-i</span> Docs/req42-container-toolchain.adoc <span class="se">\</span>
    <span class="nt">-i</span> Docs/arc42-container-toolchain.adoc <span class="se">\</span>
    <span class="nt">--strict</span> <span class="nt">--verbose</span>

<span class="c"># Bauen</span>
docker compose <span class="nb">exec </span>doctoolchain <span class="se">\</span>
    bash <span class="nt">-c</span> <span class="s1">'doctoolchain . generatePDF $DTC_OPTS'</span>
docker compose <span class="nb">exec </span>doctoolchain <span class="se">\</span>
    bash <span class="nt">-c</span> <span class="s1">'doctoolchain . generateHTML $DTC_OPTS'</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Oder mit der devcontainer-CLI&#8201;&#8212;&#8201;zumindest für den adoc-Container:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash">devcontainer <span class="nb">exec</span> <span class="nt">--workspace-folder</span> <span class="nb">.</span> validate <span class="nt">-i</span> Docs/<span class="k">*</span>.adoc <span class="nt">--strict</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Für den doctoolchain-Container geht das (noch) nicht:
<code>devcontainer exec</code> kennt nur den Primary-Service, kein <code>--service</code>-Flag.
Für den Build bleibt <code>docker compose exec</code>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="der-nebeneffekt-40-sekunden-2-sekunden">Der Nebeneffekt: 40 Sekunden → 2 Sekunden</h2>
<div class="sectionbody">
<div class="paragraph">
<p>docToolchain basiert auf Gradle.  Jeder <code>docker run</code> startet eine neue
JVM, initialisiert den Gradle-Daemon, lädt Plugins&#8201;&#8212;&#8201;rund 40 Sekunden,
bevor überhaupt etwas gebaut wird.</p>
</div>
<div class="paragraph">
<p>Der <code>dtcw</code>-Wrapper setzt bewusst <code>--no-daemon</code>, weil der Daemon bei
Einmal-Containern sowieso stirbt.  Wenn der Container aber <em>lebt</em>
(<code>sleep infinity</code> + <code>docker compose exec</code>), bleibt der Daemon warm:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top"></th>
<th class="tableblock halign-left valign-top">Erster Aufruf</th>
<th class="tableblock halign-left valign-top">Folgeaufrufe</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>docker run --no-daemon</code> (klassisch)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">~40s</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">~40s</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>docker compose exec</code> (persistent)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">~15s</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>~2s</strong></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Der Gradle-Cache liegt in einem Named Volume (<code>dtc-gradle-cache</code>) und
überlebt <code>docker compose down</code> / <code>up</code>-Zyklen.  Nur <code>docker compose down -v</code>
räumt ihn ab.</p>
</div>
<div class="paragraph">
<p>Das ist kein Trick&#8201;&#8212;&#8201;es ist schlicht die Arbeitsweise, für die Gradle
entworfen wurde.  <code>docker run</code> erzwingt den Cold-Start, den Gradle
eigentlich vermeiden will.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ci-dieselben-images-anderer-ablauf">CI: Dieselben Images, anderer Ablauf</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In der CI-Pipeline (Jenkins, GitLab CI, &#8230;&#8203;) laufen die Container als
Einmal-Aufrufe.  Der warme Daemon bringt dort nichts&#8201;&#8212;&#8201;dafür ist die
Validierung als Gate entscheidend:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code>Stage: Validate    →  Stage: Build PDF  →  Stage: Build HTML  →  Archive
(ghcr.io/tpo42/adoc) (doctoolchain)       (doctoolchain)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Wenn Validate fehlschlägt, laufen die Build-Stages nicht.  Kein
kaputtes PDF in den Artefakten.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="was-ich-dabei-gelernt-habe">Was ich dabei gelernt habe</h2>
<div class="sectionbody">
<div class="dlist">
<dl>
<dt class="hdlist1"><code>sleep infinity</code> ist keine Krücke</dt>
<dd>
<p>Bei Task-Runner-Containern (validate, build) klingt ein dauerhaft
laufender Container nach Verschwendung.  Aber wenn der Container
zustandsbehaftet ist (Gradle-Daemon, Gem-Cache), ist das persistente
Setup die performante Variante.</p>
</dd>
<dt class="hdlist1"><code>DTC_OPTS</code> als Environment-Variable</dt>
<dd>
<p>docToolchain erwartet diverse Flags (<code>-PmainConfigFile=&#8230;&#8203;</code>,
<code>--warning-mode=none</code>, &#8230;&#8203;).  In der <code>docker-compose.yml</code> als
<code>DTC_OPTS</code> hinterlegt, spart man sich die Flags bei jedem Aufruf:
<code>bash -c 'doctoolchain . generatePDF $DTC_OPTS'</code>.</p>
</dd>
<dt class="hdlist1"><code>devcontainer exec</code> hat Grenzen</dt>
<dd>
<p>Die devcontainer-CLI kann nur in den Primary-Service execen.  Für
Sidecar-Container bleibt <code>docker compose exec</code>.  Ein <code>--service</code>-Flag
wäre ein sinnvolles Feature-Request an die
<a href="https://github.com/devcontainers/cli">devcontainers/cli</a>.</p>
</dd>
</dl>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ausblick">Ausblick</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Dieses Setup dokumentiert <em>nur</em> Anforderungen und Architektur&#8201;&#8212;&#8201;es gibt
keinen Quellcode zu kompilieren.  In einem Firmware-Projekt kommt
mindestens ein dritter Container dazu: der Cross-Compiler.  Und bei
Projekten mit herstellerspezifischer Code-Generierung (STM32CubeMX)
ein vierter, der Artefakte über ein Shared Volume an den
Compile-Container weitergibt.</p>
</div>
<div class="paragraph">
<p>Die <code>docker-compose.yml</code> skaliert dafür linear&#8201;&#8212;&#8201;ein Service pro
Concern, dieselbe Compose-Datei für Devcontainer und CI.</p>
</div>
<div class="paragraph">
<p>Aber dazu mehr, wenn es soweit ist.</p>
</div>
</div>
</div>]]></content><author><name>Jens Rehsack</name></author><category term="devcontainer" /><category term="docker-compose" /><category term="asciidoc" /><category term="doctoolchain" /><category term="ci" /><category term="tpo42" /><summary type="html"><![CDATA[Im letzten Post ging es um einen einzelnen Container für Jekyll + AsciiDoc. Diesmal wird es spannender: zwei Container, die zusammenarbeiten&#8201;&#8212;&#8201;einer validiert, der andere baut.]]></summary></entry><entry><title type="html">Yocto für Bare-Metal: Cross-Compiler und CI-Toolchain aus einem Guss</title><link href="https://rehsack.de/2026/04/09/yocto-bare-metal-sdk-aus-einem-guss/" rel="alternate" type="text/html" title="Yocto für Bare-Metal: Cross-Compiler und CI-Toolchain aus einem Guss" /><published>2026-04-09T00:00:00+00:00</published><updated>2026-04-09T00:00:00+00:00</updated><id>https://rehsack.de/2026/04/09/yocto-bare-metal-sdk-aus-einem-guss</id><content type="html" xml:base="https://rehsack.de/2026/04/09/yocto-bare-metal-sdk-aus-einem-guss/"><![CDATA[<div class="paragraph">
<p>Wer Firmware für Mikrocontroller baut, braucht einen Cross-Compiler.
Den bekommt man mit <a href="https://crosstool-ng.github.io/">crosstool-ng</a>,
als ARM-Download oder aus dem Paketmanager der Distribution.
Das funktioniert&#8201;&#8212;&#8201;und wenn man dann noch Unit-Tests in der CI
haben will, wird es richtig spannend.</p>
</div>
<div class="paragraph">
<p>Denn dann braucht man <em>zwei</em> Toolchains: eine für das Target
(<code>arm-none-eabi-gcc</code> mit newlib) und eine für den Host (<code>x86_64</code>
oder <code>aarch64</code>), mit der man denselben Code gegen ein Test-Framework
kompiliert.  Auf dem Host läuft natürlich eine andere libc&#8201;&#8212;&#8201;aber
GCC-Generation, Google-Test-Version, gcov und die
Analyse-Werkzeuge müssen auf Punkt und Komma zusammenpassen.</p>
</div>
<div class="paragraph">
<p>Bare-Metal klingt dabei nach wenig Aufwand: Wenn newlib schon das
Maß der Dinge ist, ist der Horizont quasi anfassbar.  Die
Herausforderung steckt nicht in der Menge der Abhängigkeiten,
sondern darin, dass die Werkzeuge drumherum für Target und Host
aus einem Guss sein müssen.  Genau das lässt sich elegant lösen.</p>
</div>
<div class="sect1">
<h2 id="das-problem-zwei-toolchains-eine-wahrheit">Das Problem: Zwei Toolchains, eine Wahrheit</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Nehmen wir an, der Cross-Compiler ist ein GCC 15 mit newlib.
Die Unit-Tests laufen auf dem CI-Host, also brauchen wir dort
ebenfalls einen GCC 15&#8201;&#8212;&#8201;nicht den GCC 14, den Fedora 43 mitbringt,
und schon gar nicht den GCC 13 aus dem Ubuntu-LTS der CI-Runner.</p>
</div>
<div class="paragraph">
<p>Dazu kommen ABI-gekoppelte Werkzeuge:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Google Test / GMock</strong>&#8201;&#8212;&#8201;kompiliert gegen die jeweilige Toolchain</p>
</li>
<li>
<p><strong>gcov / lcov</strong>&#8201;&#8212;&#8201;müssen zur GCC-Version passen, sonst stimmt die Coverage nicht</p>
</li>
<li>
<p><strong>valgrind</strong>&#8201;&#8212;&#8201;muss die ABI der getesteten Binaries verstehen</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Wenn man das manuell pflegt, landen Target- und Host-Toolchain
schnell in getrennten Beschaffungsketten.  Das muss nicht sein.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="warum-ich-da-heute-anders-rangehe">Warum ich da heute anders rangehe</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Vor einigen Jahren bei einem Telekommunikationsausrüster: Ein SoM mit
Netzwerk-Beschleunigern, dazu ein Binary-SDK vom Chip-Hersteller.
32-Bit, eine uralte GCC-Version, gelinkt gegen eine
Linux-Distribution, die es nur noch auf dem Schwarzmarkt gegen Kamele
gab.  Lange bevor LLVM/Clang als ernstzunehmende GCC-Konkurrenz die
Stagnation aufmischte.</p>
</div>
<div class="paragraph">
<p>Die IT betrieb dafür einen Multiprozessor-Knoten mit rund 64 VMs&#8201;&#8212;&#8201;je 2&#160;VCPUs, 2&#160;GB RAM, komplett überbucht, kaum wartbar.
Das SDK ließ sich nicht reproduzieren, nicht aktualisieren und nicht
auf eine andere Host-Plattform portieren.  Dabei war der Chip sogar
Linux-fähig&#8201;&#8212;&#8201;da wäre deutlich mehr gegangen.</p>
</div>
<div class="paragraph">
<p>Das Muster habe ich seitdem mehrfach gesehen:
Chip-Hersteller wollen Chips verkaufen, keine Entwicklungsumgebungen.
Das SDK ist Beiwerk&#8201;&#8212;&#8201;es erleichtert den Einstieg, soll aber nicht
langfristig tragen.  Ein Hersteller-BSP auf LTS-Basis, jahrelang gut
genug&#8201;&#8212;&#8201;und wenn der LTS-Zyklus ausläuft, heißt es: Updaten, in
sechs Wochen liefern, nichts darf kosten.</p>
</div>
<div class="paragraph">
<p>Das hat mich motiviert, einen anderen Weg zu gehen.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="die-yocto-lösung-machine-abstraktion-als-ssot">Die Yocto-Lösung: MACHINE-Abstraktion als SSOT</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Das <a href="https://www.yoctoproject.org/">Yocto Project</a> ist primär als
Build-System für Embedded-Linux-Distributionen bekannt.  Aber sein
Rezept-System kann mehr: Es baut <em>beliebige</em> Toolchains aus den
Quellen&#8201;&#8212;&#8201;und zwar für verschiedene Zielarchitekturen aus denselben
Rezepten.</p>
</div>
<div class="paragraph">
<p>Der Schlüssel ist die <code>MACHINE</code>-Variable.  Dieselben Rezepte für GCC,
newlib, Google Test und gcov erzeugen je nach Target:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 40%;">
<col style="width: 40%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">MACHINE</th>
<th class="tableblock halign-left valign-top">Ergebnis</th>
<th class="tableblock halign-left valign-top">Beispiel</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>aducm360</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>arm-none-eabi-gcc</code> (Cortex-M3, soft-float)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Mess-MCU, ADC-Frontend</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>stm32h7a3</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>arm-none-eabi-gcc</code> (Cortex-M7, hard-float, FPv5-D16)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kommunikations-MCU, DCP-Stack</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>samd21</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>arm-none-eabi-gcc</code> (Cortex-M0+, soft-float)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kamera-Steuerung</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>esp32c6</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>riscv32-none-elf-gcc</code> (RV32IMAC)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">RISC-V IoT, Wi-Fi/BLE</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>gd32vf103</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>riscv32-none-elf-gcc</code> (RV32IMAC)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">RISC-V mit DSP-Erweiterungen, atomthreads</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>x86-64</code> / <code>aarch64</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Host-nativer GCC derselben Generation</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">CI: Unit-Tests, valgrind, Coverage</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Die Liste ließe sich fortsetzen.  Der Punkt ist: Ob ARM Cortex-M, RISC-V oder Host-nativ&#8201;&#8212;&#8201;die Rezepte sind dieselben.  Nur <code>MACHINE</code> und die zugehörige
Tune-Konfiguration unterscheiden sich.</p>
</div>
<div class="paragraph">
<p><strong>Eine</strong> Quelle, <strong>eine</strong> GCC-Version, <strong>eine</strong> ABI-Kopplung&#8201;&#8212;&#8201;DRY und
SSOT statt n&#160;getrennt gepflegte Toolchains.</p>
</div>
<div class="paragraph">
<p>Yocto baut dabei nicht nur den Compiler: Auch ein <strong>QEMU</strong> für das
jeweilige Target lässt sich aus denselben Rezepten erzeugen.  Damit
kann man Firmware-Binaries auf dem CI-Host emulieren&#8201;&#8212;&#8201;ohne echte
Hardware, aber mit der richtigen CPU-Emulation.  Für
Integrationstests auf einem Cortex-M oder RISC-V ist das Gold wert.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="was-gehört-ins-sdkund-was-nicht">Was gehört ins SDK&#8201;&#8212;&#8201;und was nicht?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Die erste Intuition ist: Nur der Compiler und seine unmittelbaren
Abhängigkeiten.  In der Praxis zieht man die Grenze weiter.</p>
</div>
<div class="paragraph">
<p><strong>Aus Yocto&#8201;&#8212;&#8201;eingefroren und versioniert:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>GCC + binutils + newlib (der Compiler-Kern)</p>
</li>
<li>
<p>Google Test / GMock (Test-Framework, gegen die Toolchain kompiliert)</p>
</li>
<li>
<p>gcov / lcov (Coverage&#8201;&#8212;&#8201;muss zur GCC-Version passen)</p>
</li>
<li>
<p>valgrind (Speicheranalyse&#8201;&#8212;&#8201;muss die ABI verstehen)</p>
</li>
<li>
<p><strong>clang-tidy</strong> (statische Analyse&#8201;&#8212;&#8201;braucht die <em>richtigen</em> Header
und Bibliotheken des Targets, nicht die des Host-Systems)</p>
</li>
<li>
<p><strong>clang-format</strong> (eine eingefrorene Version verhindert
Whitespace-Kriege zwischen Entwicklern)</p>
</li>
<li>
<p><strong>CMake, Ninja</strong> (wenn das CMake der Host-Distribution plötzlich
ein Major-Update bekommt, spuckt es unter Umständen in den Build)</p>
</li>
<li>
<p>QEMU (Target-Emulation für Integrationstests)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Der gemeinsame Nenner: Alles, was das Build-Ergebnis oder die
Analyse des Build-Ergebnisses beeinflusst, muss einfrierbar und
reproduzierbar sein.  clang-tidy klingt zunächst orthogonal&#8201;&#8212;&#8201;bis
man merkt, dass es die Target-Header parsen muss, nicht die des
Host-Systems.</p>
</div>
<div class="paragraph">
<p><strong>Aus der Distribution&#8201;&#8212;&#8201;unabhängig aktualisierbar:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Doxygen (API-Dokumentation)</p>
</li>
<li>
<p>srecord (ELF → HEX-Konvertierung)</p>
</li>
<li>
<p>doctoolchain, asciidoctor (Projektdokumentation)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Diese Werkzeuge beeinflussen weder das Binary noch die Analyse und
können unabhängig aktualisiert werden&#8201;&#8212;&#8201;ohne
Firmware-Requalifizierung.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="bare-metal-hardening-was-geht-ohne-os">Bare-Metal-Hardening: Was geht ohne OS?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Ein Nebeneffekt der Yocto-basierten Toolchain: Man hat volle
Kontrolle über die Compiler-Flags, und zwar in sauberen Schichten.</p>
</div>
<div class="paragraph">
<p>Auf Bare-Metal fehlen die üblichen OS-Schutzmechanismen
(<code><em>_stack_chk_fail</code> ohne libc, kein ASLR ohne Loader, kein PIE ohne
MMU).  Aber GCC 13+ bietet Hardening-Flags, die _ohne</em> OS
funktionieren:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash"><span class="nt">-fharden-compares</span>
<span class="nt">-fharden-conditional-branches</span>
<span class="nt">-fharden-control-flow-redundancy</span>
<span class="nt">-ftrivial-auto-var-init</span><span class="o">=</span>zero</code></pre>
</div>
</div>
<div class="paragraph">
<p>Diese Flags landen nicht in der Toolchain selbst (sonst würde newlib
nicht mehr bauen), sondern in einem SDK-Environment-Hook&#8201;&#8212;&#8201;einer
dünnen Shell-Datei, die beim Aktivieren der Toolchain die
<code>CFLAGS</code>/<code>CXXFLAGS</code> setzt.  Drei Schichten:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Distro-Config</strong> (Yocto): Flags, die auch newlib braucht (<code>-ffunction-sections</code>, <code>-fdata-sections</code>)</p>
</li>
<li>
<p><strong>SDK-Hook</strong>: Hardening und Warnungen für die Firmware-Entwicklung</p>
</li>
<li>
<p><strong>Firmware-Projekt</strong> (CMakeLists.txt): Projektspezifisches (<code>-std=gnu23</code>, Target-Defines)</p>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="langzeitwirkung-10-jahre-firmware-support">Langzeitwirkung: 10 Jahre Firmware-Support</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In regulierten Branchen liefert man Firmware-Fixes für Produkte,
die vor 10 oder 15 Jahren ausgeliefert wurden.  Mit einer
Yocto-basierten Toolchain ist das ein <code>git checkout</code> auf den
Release-Stand und ein <code>bitbake meta-toolchain</code>&#8201;&#8212;&#8201;fertig.</p>
</div>
<div class="paragraph">
<p>Yocto liefert dafür zwei Mechanismen, die oft als Implementierungs-
detail abgetan werden, aber für das Release-Management essentiell sind:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>DL_DIR</code></dt>
<dd>
<p>Das Verzeichnis, in das Yocto alle heruntergeladenen
Quellen ablegt&#8201;&#8212;&#8201;Tarballs, Git-Snapshots, Patches.  Das ist kein
Nebenprodukt, das ist ein <strong>Quellenarchiv</strong>.  Wer <code>DL_DIR</code> archiviert,
kann die Toolchain auch dann noch bauen, wenn die Upstream-Server
längst offline sind.</p>
</dd>
<dt class="hdlist1"><code>SSTATE_DIR</code></dt>
<dd>
<p>Der Shared-State-Cache.  Yocto speichert hier die
Zwischenergebnisse jedes Build-Schritts.  Bei einem Release-Fix
Jahre später muss nicht alles von Grund auf neu gebaut werden&#8201;&#8212;&#8201;der Cache beschleunigt den Rebuild auf die tatsächlich geänderten
Komponenten.</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>Beide Verzeichnisse zusammen bilden das Rückgrat für langfristige
Nachlieferbarkeit: Quellen gesichert, Build-Ergebnisse
nachvollziehbar, Rebuild-Zeiten beherrschbar.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="cra-und-sbom-vom-nice-to-have-zur-pflicht">CRA und SBOM: Vom Nice-to-have zur Pflicht</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Mit dem <a href="https://digital-strategy.ec.europa.eu/en/policies/cyber-resilience-act">Cyber Resilience Act</a>
wird die Frage „Was steckt in meiner Toolchain?" regulatorisch
relevant.  Ein SBOM (Software Bill of Materials) mit voller Provenance
ist keine Kür mehr&#8201;&#8212;&#8201;es wird Pflicht für Produkte mit digitalen
Elementen.</p>
</div>
<div class="paragraph">
<p>Yocto erzeugt SPDX-SBOMs als Nebenprodukt des Builds&#8201;&#8212;&#8201;für jede
Komponente, die aus den Quellen gebaut wurde.  <code>DL_DIR</code> liefert die
Quellenarchivierung, die Rezepte liefern die Abhängigkeitskette,
und <code>INHERIT += "create-spdx"</code> bindet beides zu einem maschinenlesbaren
SBOM zusammen.</p>
</div>
<div class="paragraph">
<p>Aus Yocto gebaut heißt: Die Provenance stimmt per Konstruktion,
nicht per Vertrauen.  Das ist ein echtes Pfund, wenn der Auditor
vor der Tür steht.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="fazit">Fazit</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Cross-Compiler für Bare-Metal gibt es an jeder Ecke.  Der Mehrwert
von Yocto liegt nicht im Cross-Compiler selbst, sondern darin, dass
Target-Toolchain, Host-CI-Toolchain und alle Werkzeuge drumherum
<em>aus derselben Quelle</em> kommen&#8201;&#8212;&#8201;reproduzierbar, einfrierbar,
CRA-ready.</p>
</div>
<div class="paragraph">
<p>Die Einstiegshürde ist real: Yocto hat eine steile Lernkurve.  Aber
wer den Schritt wagt, bekommt eine Toolchain-Infrastruktur, die
mit jedem neuen Target und jedem neuen Projekt mitträgt&#8201;&#8212;&#8201;statt
jedes Mal bei null anzufangen.</p>
</div>
</div>
</div>]]></content><author><name>Jens Rehsack</name></author><category term="yocto" /><category term="embedded" /><category term="bare-metal" /><category term="ci" /><category term="toolchain" /><category term="cross-compilation" /><summary type="html"><![CDATA[Wer Firmware für Mikrocontroller baut, braucht einen Cross-Compiler. Den bekommt man mit crosstool-ng, als ARM-Download oder aus dem Paketmanager der Distribution. Das funktioniert&#8201;&#8212;&#8201;und wenn man dann noch Unit-Tests in der CI haben will, wird es richtig spannend.]]></summary></entry><entry><title type="html">Jekyll + AsciiDoc im Devcontainer — null lokales Setup</title><link href="https://rehsack.de/2026/04/05/devcontainer-asciidoc-jekyll/" rel="alternate" type="text/html" title="Jekyll + AsciiDoc im Devcontainer — null lokales Setup" /><published>2026-04-05T00:00:00+00:00</published><updated>2026-04-05T00:00:00+00:00</updated><id>https://rehsack.de/2026/04/05/devcontainer-asciidoc-jekyll</id><content type="html" xml:base="https://rehsack.de/2026/04/05/devcontainer-asciidoc-jekyll/"><![CDATA[<div class="paragraph">
<p>Eine Jekyll-Site mit AsciiDoc-Unterstützung aufzusetzen bedeutet normalerweise:
Ruby installieren, Bundler, eine Handvoll Gems.  Mit dem
<a href="https://github.com/tpo42/tpo42-asciidoc-container">tpo42/adoc</a>-Container und der
<a href="https://containers.dev/">Dev-Containers</a>-Spezifikation kann man sich das alles
sparen.</p>
</div>
<div class="sect1">
<h2 id="die-idee">Die Idee</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Der Container <code>ghcr.io/tpo42/adoc</code> bringt Ruby 3.x, Bundler 4.x und die
komplette Asciidoctor-Toolchain mit (asciidoctor, asciidoctor-pdf,
asciidoctor-diagram, …).  Jekyll und seine Plugins sind nur ein
<code>bundle install</code> entfernt&#8201;&#8212;&#8201;kein Grund, das Host-System zuzumüllen.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="setup">Setup</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="1-gemfile">1. Gemfile</h3>
<div class="paragraph">
<p>Ein Standard-Jekyll-Gemfile&#8201;&#8212;&#8201;der Container liefert Ruby und Bundler, auf
dem Host wird nichts weiter benötigt:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="ruby"><span class="n">source</span> <span class="s2">"https://rubygems.org"</span>

<span class="n">gem</span> <span class="s2">"jekyll"</span><span class="p">,</span> <span class="s2">"~&gt; 4.4"</span>
<span class="n">gem</span> <span class="s2">"jekyll-remote-theme"</span>
<span class="n">gem</span> <span class="s2">"jekyll-asciidoc"</span>
<span class="n">gem</span> <span class="s2">"asciidoctor"</span>
<span class="n">gem</span> <span class="s2">"jekyll-sitemap"</span>
<span class="n">gem</span> <span class="s2">"jekyll-feed"</span>
<span class="n">gem</span> <span class="s2">"jekyll-include-cache"</span>
<span class="n">gem</span> <span class="s2">"webrick"</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="2-devcontainerdevcontainer-json">2. <code>.devcontainer/devcontainer.json</code></h3>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="json"><span class="p">{</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"meine-site"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"image"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ghcr.io/tpo42/adoc:latest"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"workspaceFolder"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/workspace"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"workspaceMount"</span><span class="p">:</span><span class="w"> </span><span class="s2">"source=${localWorkspaceFolder},target=/workspace,type=bind"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"postCreateCommand"</span><span class="p">:</span><span class="w"> </span><span class="s2">"bundle config set --local path vendor/bundle &amp;&amp; bundle install"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"appPort"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">4000</span><span class="p">,</span><span class="w"> </span><span class="mi">35729</span><span class="p">],</span><span class="w">
  </span><span class="nl">"forwardPorts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">4000</span><span class="p">,</span><span class="w"> </span><span class="mi">35729</span><span class="p">],</span><span class="w">
  </span><span class="nl">"portsAttributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"4000"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jekyll"</span><span class="p">,</span><span class="w"> </span><span class="nl">"onAutoForward"</span><span class="p">:</span><span class="w"> </span><span class="s2">"notify"</span><span class="w"> </span><span class="p">},</span><span class="w">
    </span><span class="nl">"35729"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"LiveReload"</span><span class="p">,</span><span class="w"> </span><span class="nl">"onAutoForward"</span><span class="p">:</span><span class="w"> </span><span class="s2">"silent"</span><span class="w"> </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Die wichtigsten Punkte:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>image</code></dt>
<dd>
<p>Nutzt direkt <code>ghcr.io/tpo42/adoc:latest</code> (der "einfache Fall" aus
ADR-005).  Kein eigenes Dockerfile nötig.</p>
</dd>
<dt class="hdlist1"><code>postCreateCommand</code></dt>
<dd>
<p>Installiert Gems nach <code>vendor/bundle</code> (schreibbar für
den Container-User, von <code>.gitignore</code> ignoriert).</p>
</dd>
<dt class="hdlist1"><code>appPort</code></dt>
<dd>
<p>Mappt Ports auf Docker-Ebene, damit <code>jekyll serve</code> auch ohne
VS Code vom Host aus erreichbar ist.</p>
</dd>
<dt class="hdlist1"><code>forwardPorts</code></dt>
<dd>
<p>Dasselbe für VS Codes eingebautes Port-Forwarding.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="3-container-starten">3. Container starten</h3>
<div class="paragraph">
<p>Mit der <a href="https://github.com/devcontainers/cli">devcontainer-CLI</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash">devcontainer up <span class="nt">--workspace-folder</span> .</code></pre>
</div>
</div>
<div class="paragraph">
<p>Dann die Site starten:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash">devcontainer <span class="nb">exec</span> <span class="nt">--workspace-folder</span> <span class="nb">.</span> <span class="se">\</span>
  bundle <span class="nb">exec </span>jekyll serve <span class="nt">--host</span> 0.0.0.0 <span class="nt">--livereload</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="http://localhost:4000" class="bare">http://localhost:4000</a> öffnen&#8201;&#8212;&#8201;fertig.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="warum-kein-eigenes-jekyll-image">Warum kein eigenes Jekyll-Image?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Images wie <code>bretfisher/jekyll-serve</code> funktionieren, bringen aber ihr eigenes
Ruby und Gem-Set mit.  Wenn der Content AsciiDoc ist, braucht man die
Asciidoctor-Toolchain <em>und</em> Jekyll&#8201;&#8212;&#8201;zwei getrennte Container oder ein
aufgeblähtes Custom-Image.</p>
</div>
<div class="paragraph">
<p>Mit <code>ghcr.io/tpo42/adoc</code> als Basis bekommt man Asciidoctor für die
Dokumentenverarbeitung (flatten, validate, Diagramme extrahieren via <code>adcw</code>)
<em>und</em> Jekyll zum Servieren&#8201;&#8212;&#8201;aus einem einzigen Image, gesteuert über das
Gemfile des Projekts.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="asciidoc-validierunggeht-auch-ohne-devcontainer">AsciiDoc-Validierung&#8201;&#8212;&#8201;geht auch ohne Devcontainer</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Der <code>adcw</code>-Wrapper braucht keinen Devcontainer.  <code>.adoc</code>-Dateien lassen sich
jederzeit validieren:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="bash">adcw validate <span class="nt">-i</span> _pages/about.adoc</code></pre>
</div>
</div>
<div class="paragraph">
<p>Das startet einen kurzen <code>docker run --rm</code>&#8201;&#8212;&#8201;kein langlebiger Container nötig.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="was-ich-dabei-gelernt-habe">Was ich dabei gelernt habe</h2>
<div class="sectionbody">
<div class="dlist">
<dl>
<dt class="hdlist1"><code>appPort</code> vs <code>forwardPorts</code></dt>
<dd>
<p>Die devcontainer-CLI leitet Ports nicht selbst weiter.  <code>forwardPorts</code> ist ein
VS-Code-Feature.  Für reine CLI-Workflows braucht man <code>appPort</code>.</p>
</dd>
<dt class="hdlist1">Gem-Berechtigungen</dt>
<dd>
<p>Der <code>ghcr.io/tpo42/adoc</code>-Container läuft als unprivilegierter User.
<code>bundle install</code> ohne <code>--path</code> versucht in System-Gem-Verzeichnisse zu
schreiben und scheitert.  <code>bundle config set --local path vendor/bundle</code> löst
das sauber.</p>
</dd>
<dt class="hdlist1">Container-Wiederverwendung</dt>
<dd>
<p><code>devcontainer up</code> nutzt bestehende Container wieder.  Nach Änderungen an
<code>devcontainer.json</code> muss der alte Container erst entfernt werden
(<code>docker rm -f &lt;id&gt;</code>), bevor <code>devcontainer up</code> erneut funktioniert.</p>
</dd>
</dl>
</div>
</div>
</div>]]></content><author><name>Jens Rehsack</name></author><category term="devcontainer" /><category term="jekyll" /><category term="asciidoc" /><category term="docker" /><category term="tpo42" /><summary type="html"><![CDATA[Eine Jekyll-Site mit AsciiDoc-Unterstützung aufzusetzen bedeutet normalerweise: Ruby installieren, Bundler, eine Handvoll Gems. Mit dem tpo42/adoc-Container und der Dev-Containers-Spezifikation kann man sich das alles sparen.]]></summary></entry><entry><title type="html">Vermögens- und Erbschaftssteuer nach Art. 14 GG — ein Zwei-Säulen-Modell</title><link href="https://rehsack.de/2026/03/12/vermoegens-erbschaftssteuer-grundgesetz/" rel="alternate" type="text/html" title="Vermögens- und Erbschaftssteuer nach Art. 14 GG — ein Zwei-Säulen-Modell" /><published>2026-03-12T00:00:00+00:00</published><updated>2026-03-12T00:00:00+00:00</updated><id>https://rehsack.de/2026/03/12/vermoegens-erbschaftssteuer-grundgesetz</id><content type="html" xml:base="https://rehsack.de/2026/03/12/vermoegens-erbschaftssteuer-grundgesetz/"><![CDATA[<div class="paragraph">
<p>Die Macher von <a href="https://www.vergnuegt.info" target="_blank" rel="noopener">vergnügt</a> (<em>Vermögens- und
Erbschaftsteuer retten Gemeinden in Not über gerechte Talerabgabe!</em>) haben mich
inspiriert, einen Gedanken, der mich über Jahre nicht losließ, im Gespräch
herausfordern zu lassen und schließlich aufzuschreiben.  Ich hoffe, es
bereichert die Diskussion&#8201;&#8212;&#8201;und nun viel Vergnügen.</p>
</div>
<hr>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Eigentum verpflichtet.  Sein Gebrauch soll zugleich dem Wohle der Allgemeinheit
dienen.</p>
</div>
</blockquote>
<div class="attribution">
&#8212; Art. 14 Abs. 2 GG
</div>
</div>
<div class="paragraph">
<p>Dieses Modell nimmt diese Norm beim Wort und leitet daraus ein operatives
Steuerprinzip ab: Vermögen, das dem Allgemeinwohl dient, ist steuerlich bedient.
Vermögen, das dies nicht tut, wird besteuert.</p>
</div>
<div class="sect1">
<h2 id="grundprinzip">Grundprinzip</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Die zentrale Unterscheidung ist nicht die <em>Höhe</em> des Vermögens, sondern seine
<em>Funktion</em>.  Die Frage lautet nicht &#8220;Wie viel besitzt jemand?&#8221;, sondern
&#8220;Trägt dieses Vermögen zum Allgemeinwohl bei&#8201;&#8212;&#8201;oder nicht?&#8221;</p>
</div>
<div class="paragraph">
<p>Vermögen gilt als gemeinwohldienlich, wenn es mindestens eines der folgenden
Kriterien erfüllt:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Es erzeugt Arbeitsplätze durch direkte, persönlich verantwortete Inhaberschaft
(Beispiel: inhabergeführter Mittelständler wie Trigema).</p>
</li>
<li>
<p>Es generiert versteuerte Erträge&#8201;&#8212;&#8201;Dividenden, Mieteinnahmen,
Betriebsgewinne&#8201;&#8212;&#8201;und wird damit bereits durch bestehende Steuern erfasst.</p>
</li>
<li>
<p>Es ist gemeinnützig gewidmet oder der Allgemeinheit zugänglich (Beispiel:
Naturschutzgebiet, öffentlich zugängliches Kulturgut).</p>
</li>
<li>
<p>Es zeigt messbares Wachstum über einen Mehrjahreszeitraum (3–5 Jahre), was auf
produktive Kapitalverwendung hindeutet.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="säule-1-vermögenssteuer-als-auffangnetz">Säule 1: Vermögenssteuer als Auffangnetz</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong>Was besteuert wird:</strong> Ausschließlich Vermögen, das durch keine bestehende
Steuer erfasst wird <em>und</em> keinen messbaren gesellschaftlichen Nutzen erzeugt.</p>
</div>
<div class="paragraph">
<p><strong>Was nicht besteuert wird:</strong> Jedes Vermögen, das bereits über
Kapitalertragsteuer, Einkommensteuer, Gewerbesteuer oder andere Abgaben zum
Gemeinwohl beiträgt.  Ebenso Vermögen, das gemeinnützig gewidmet ist oder durch
aktive Inhaberführung Arbeitsplätze schafft.</p>
</div>
<div class="paragraph">
<p><strong>Steuersatz:</strong> 1–2&#160;% jährlich auf das nicht-gemeinwohldienliche Vermögen.</p>
</div>
<div class="paragraph">
<p><strong>Freibetrag:</strong> Eine großzügig bemessene Lebensbedarfsgrenze, die Wohnen,
Bildung, Vorsorge und einen substanziellen Sicherheitspuffer abdeckt.  Alles
darunter ist grundsätzlich von der Vermögenssteuer befreit.</p>
</div>
<div class="paragraph">
<p><strong>Bewertungszeitraum:</strong> Die Gemeinwohldienlichkeit wird über einen rollierenden
3- bis 5-Jahreszeitraum bewertet, um konjunkturelle Schwankungen und
Wirtschaftskrisen nicht sinnlos zu verschärfen.</p>
</div>
<div class="paragraph">
<p><strong>Kernmerkmal:</strong> Es gibt per Design keine Doppelbesteuerung, da die
Vermögenssteuer nur in der Lücke greift, die bestehende Steuern nicht abdecken.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="säule-2-erbschaftssteuer-nach-gleichem-prinzip">Säule 2: Erbschaftssteuer nach gleichem Prinzip</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Die Erbschaftssteuer folgt derselben Logik: Wer beiträgt, trägt bei.  Wer noch
nicht beiträgt, hat jetzt die Chance.</p>
</div>
<div class="paragraph">
<p><strong>Vererbung eines operativen Unternehmens:</strong> Wer ein Unternehmen erbt und es
mindestens 10 Jahre in direkter Inhaberschaft fortführt, zahlt keine
Erbschaftssteuer.  Der gesellschaftliche Beitrag wird durch den Betrieb
erbracht.  Bei Verkauf nach Ablauf der Frist wird der Verkaufserlös als
reguläres Einkommen versteuert.</p>
</div>
<div class="paragraph">
<p><strong>Vererbung von passivem Vermögen:</strong> Wer ein Aktiendepot, Sachwerte oder andere
passive Vermögenswerte erbt, versteuert diese bei Veräußerung als Einkommen.  Es
gibt keinen Anspruch auf steuerfreie Weitergabe von Vermögen, das keinen aktiven
Beitrag zum Allgemeinwohl leistet.</p>
</div>
<div class="paragraph">
<p><strong>Entscheidende Unterscheidung:</strong> Direkte, persönlich verantwortete Inhaberschaft
wird privilegiert.  Anonyme Anteilseignerschaft&#8201;&#8212;&#8201;also die bloße Vererbung von
Kapitalbeteiligungen ohne operative Verantwortung&#8201;&#8212;&#8201;wird behandelt wie jedes
andere passive Vermögen.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="was-dieses-modell-nicht-ist">Was dieses Modell nicht ist</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong>Keine Neidsteuer.</strong> Das Modell richtet sich nicht gegen Reichtum, sondern
gegen brachliegendes Vermögen.  Wer mit seinem Vermögen wirtschaftet,
Arbeitsplätze schafft oder Erträge versteuert, wird nicht zusätzlich belastet.</p>
</div>
<div class="paragraph">
<p><strong>Keine Leistungsfeindlichkeit.</strong> Im Gegenteil: Das Modell privilegiert aktives,
produktives Eigentum gegenüber passivem Besitz.  Es belohnt den Unternehmer und
besteuert den Rentier.</p>
</div>
<div class="paragraph">
<p><strong>Keine Umverteilungsideologie.</strong> Der Ausgangspunkt ist das Grundgesetz, nicht
ein politisches Programm.  Art. 14 Abs. 2 GG formuliert eine Pflicht&#8201;&#8212;&#8201;dieses
Modell operationalisiert sie.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="offene-fragen-bewusst-transparent-gehalten">Offene Fragen (bewusst transparent gehalten)</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong>Administrierbarkeit:</strong> Die Bewertung von &#8220;Gemeinwohldienlichkeit&#8221; erfordert
klare, einfache Kriterien.  Je komplexer das System, desto mehr profitieren
diejenigen mit den besten Steuerberatern.  Die Kriterien müssen so formuliert
sein, dass sie regelbasiert und nicht ermessensbasiert angewendet werden können.</p>
</div>
<div class="paragraph">
<p><strong>Konkrete Lebensbedarfsgrenze:</strong> Die genaue Höhe des Freibetrags muss politisch
verhandelt werden.  Ob 25 oder 40 Millionen Euro&#8201;&#8212;&#8201;die Zielgruppe beginnt
mindestens bei Faktor 10 darüber.</p>
</div>
<div class="paragraph">
<p><strong>Deklarationspflicht:</strong> Um Umgehung zu verhindern (das
&#8220;Rembrandt-auf-dem-Dachboden&#8221;-Problem), braucht es eine jährliche
Deklarationspflicht für Vermögen oberhalb der Lebensbedarfsgrenze, inklusive
Sachwerte.  Nichtdeklaration wird sanktioniert.</p>
</div>
<div class="paragraph">
<p><strong>Rechtsstaatliche Ausgestaltung:</strong> Die konkrete juristische Umsetzung&#8201;&#8212;&#8201;insbesondere Fragen der Rückwirkung und der Vereinbarkeit mit europäischem
Recht&#8201;&#8212;&#8201;erfordert fachliche Expertise und ist in diesem Konzeptpapier bewusst
nicht abschließend behandelt.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="zusammenfassung-in-einem-satz">Zusammenfassung in einem Satz</h2>
<div class="sectionbody">
<div class="paragraph">
<p><em>"`Eigentum verpflichtet`"&#8201;&#8212;&#8201;wer dieser Verpflichtung nachkommt, ist steuerlich
bedient; wer nicht, zahlt Vermögenssteuer.</em></p>
</div>
<hr>
<div class="paragraph">
<p><em>Dieses Konzept entstand aus einer strukturierten Debatte, in der die Annahmen
bewusst und systematisch stressgetestet wurden.  Es erhebt keinen Anspruch auf
juristische Vollständigkeit, sondern versteht sich als Denkrahmen für die
politische Diskussion.</em></p>
</div>
</div>
</div>]]></content><author><name>Jens Rehsack</name></author><category term="politik" /><category term="grundgesetz" /><category term="vermögenssteuer" /><category term="erbschaftssteuer" /><category term="gemeinwohl" /><summary type="html"><![CDATA[Die Macher von vergnügt (Vermögens- und Erbschaftsteuer retten Gemeinden in Not über gerechte Talerabgabe!) haben mich inspiriert, einen Gedanken, der mich über Jahre nicht losließ, im Gespräch herausfordern zu lassen und schließlich aufzuschreiben. Ich hoffe, es bereichert die Diskussion&#8201;&#8212;&#8201;und nun viel Vergnügen.]]></summary></entry><entry><title type="html">Offener Brief an Dr. Katrin Vernau — 7 Monate WDR-Intendantin, Zeit für Open Source?</title><link href="https://rehsack.de/2025/08/18/offener-brief-wdr-open-source/" rel="alternate" type="text/html" title="Offener Brief an Dr. Katrin Vernau — 7 Monate WDR-Intendantin, Zeit für Open Source?" /><published>2025-08-18T00:00:00+00:00</published><updated>2025-08-18T00:00:00+00:00</updated><id>https://rehsack.de/2025/08/18/offener-brief-wdr-open-source</id><content type="html" xml:base="https://rehsack.de/2025/08/18/offener-brief-wdr-open-source/"><![CDATA[<div class="paragraph">
<p>Liebe Frau Dr. Vernau,</p>
</div>
<div class="paragraph">
<p>Sie sind seit Januar 2025 WDR-Intendantin&#8201;&#8212;&#8201;7 Monate, um neue Akzente zu setzen.
Haben Sie schon bemerkt, was Ihre Redaktionen seit Jahren übersehen?</p>
</div>
<div class="paragraph">
<p>Lassen Sie mich Ihnen eine Geschichte erzählen, die zeigt, warum dieser Neuanfang
nötig ist:</p>
</div>
<div class="sect1">
<h2 id="2018-deutscher-perl-workshop-in-köln">2018: Deutscher Perl-Workshop in Köln</h2>
<div class="sectionbody">
<div class="paragraph">
<p>2018 schrieb ich WDR-Redaktionen anlässlich des 20. Deutschen Perl-Workshops in
Köln.  Internationale Konferenz, 200+ Teilnehmer, direkt vor der WDR-Haustür.
Die Antwort der Lokalzeit-Redaktion:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Das Thema erscheint uns zu spezialisiert.  Unsere Zuschauer sind nicht
herausragend technisch interessiert.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Aktuelle Stunde?  Keine Antwort.  Wissenschaftsredaktion?  Schweigen.
Weitere Redaktionen?  Totenstille.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="2025-die-ignoranz-bleibt-aber-die-probleme-werden-dramatischer">2025: Die Ignoranz bleibt, aber die Probleme werden dramatischer</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Gerade diese Woche: External Secrets Operator für Kubernetes vor dem Aus&#8201;&#8212;&#8201;nur
noch ein Maintainer, völlig überarbeitet.  Gernot Starke (arc42, 20 Jahre!)
berichtet: &#8220;Eine Million Downloads jährlich, hunderte Unternehmen nutzen es
täglich&#8201;&#8212;&#8201;aber Contributions?  Nahe Null.&#8221;</p>
</div>
<div class="paragraph">
<p><strong>Das ist die Realität unsichtbarer Communities: Genutzt von allen, unterstützt
von wenigen, berichtet von niemandem.</strong></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="deutsche-entwickler-sichern-globale-standards-ab">Deutsche Entwickler sichern globale Standards ab</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Während Millionen für Fußball-Übertragungen fließen, kostet ein Bericht über
deutsche Tech-Communities offenbar zu viel Aufwand für eine Antwort.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Chris Badura aus Köln portiert jährlich ~4 ARM-Architekturen für NetBSD&#8201;&#8212;&#8201;weltweit genutzt</p>
</li>
<li>
<p>Gernot Starke feiert mit arc42 das 20-jährige Jubiläum&#8201;&#8212;&#8201;<em>der</em>
Architektur-Standard in DACH</p>
</li>
<li>
<p>unByte GmbH entwickelt Self-Bootstrapping K8s&#8201;&#8212;&#8201;Kubernetes-Innovation aus NRW</p>
</li>
<li>
<p>Yocto Linux mit Contributoren aus Köln, NRW und ganz Deutschland steckt in
Milliarden von Geräten</p>
</li>
<li>
<p><a href="https://coocook.org">Coocook.org</a> hilft tausenden Ferienlagern bei der
Essensplanung</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Und das sind nur die, die mir in 30 Sekunden einfallen.  Was würde echte
Recherche zutage fördern?</p>
</div>
<div class="paragraph">
<p><strong>Aber Sie zeigen lieber die nächste Influencer-Doku.</strong></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="der-wdr-hat-einen-bildungsauftrag">Der WDR hat einen Bildungsauftrag</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Dazu gehört zu zeigen, <em>wer</em> unsere digitale Zukunft gestaltet.  Das sind nicht
nur die üblichen Verdächtigen aus dem Silicon Valley&#8201;&#8212;&#8201;das sind auch Menschen
aus Köln, Düsseldorf, Essen.</p>
</div>
<div class="paragraph">
<p><strong>Und diese Menschen gehören in die Prime-Formate, nicht nur in
Bildungs-Nischen.</strong></p>
</div>
<div class="paragraph">
<p>Stellen Sie sich vor: &#8220;Austausch in der TH Köln: Coocook-Begründer auf
Konferenz&#8221;&#8201;&#8212;&#8201;das wäre in der Lokalzeit eingeschlagen wie eine Bombe.  Echter
lokaler Bezug, echte Innovation, echte Menschen.  Hintergrund-Recherchen gern in
Bildungsformaten&#8201;&#8212;&#8201;aber die Stories müssen raus aus der Nische!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="mein-vorschlag-für-ihre-verbleibende-amtszeit">Mein Vorschlag für Ihre verbleibende Amtszeit</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Beauftragen Sie eine Redaktion mit einer Serie über deutsche Tech-Communities.
Zeigen Sie die FrOSCon, die Chemnitzer Linux-Tage, den German Perl Workshop.
Zeigen Sie die Menschen dahinter.</p>
</div>
<div class="paragraph">
<p>Nicht als &#8220;Nerd-Spektakel&#8221;, sondern als das, was es ist: Die Arbeit an unserer
gemeinsamen digitalen Zukunft.</p>
</div>
<div class="paragraph">
<p><strong>Werden Sie die Intendantin, die den Unterschied macht?</strong></p>
</div>
<div class="paragraph">
<p>Mit freundlichen Grüßen und konkreten Erwartungen,<br>
Jens Rehsack</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>Falls Sie Inspiration brauchen&#8201;&#8212;&#8201;die New York Perl Mongers schauten 2018 nach
Köln und sagten: &#8220;I had no idea how to celebrate our anniversary.  Now I know.&#8221;
Vielleicht sollte der WDR auch mal nach Köln schauen.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>]]></content><author><name>Jens Rehsack</name></author><category term="medien" /><category term="open-source" /><category term="wdr" /><category term="digitale-souveränität" /><category term="software-resilienz" /><summary type="html"><![CDATA[Liebe Frau Dr. Vernau,]]></summary></entry></feed>