Die Web-Version wird öffentlich betreibbar
Die Browserfassung war für den öffentlichen Betrieb gesperrt, und zwar
ausdrücklich: Impressum und Datenschutzerklärung trugen TODO-Blöcke statt
Angaben, der Quelltextverweis im Fuß zeigte auf
https://TODO-QUELLCODE-URL-EINTRAGEN, und der README führte beides als „vor dem
produktiven Betrieb zwingend zu erledigen“. Beide Sperren fallen; die
Auslieferung wird zugleich auf das tatsächliche Ziel eingerichtet — einen
Unterpfad einer bestehenden Domain (/aendggner/), nicht einen eigenen Server.
Impressum und Datenschutz führen nun die wirklichen Angaben: § 5 DDG (nicht mehr
§ 5 TMG) samt § 18 Abs. 2 MStV, Verantwortlicher nach Art. 4 Nr. 7 DSGVO,
Rechtsgrundlage Art. 6 Abs. 1 lit. f für die Zugriffsprotokolle, deren Frist mit
vierzehn Tagen benannt ist, sowie die Betroffenenrechte samt Aufsichtsbehörde.
Die Aussage über die Dateien der Nutzer:innen bleibt, wie sie war; sie trifft zu,
weil die Verarbeitung den Rechner nicht verlässt.
Der Quelltext wird beigelegt statt verlinkt. deploy/webpaket.sh tritt in der
Phase „package“ hinter den Übersetzer und macht aus dessen Ergebnis ein
auslieferbares Verzeichnis: Es wirft den .wat-Zwischenschritt fort (368 MB, der
bislang von Hand zu übergehen war), legt den Quelltext der gebauten Fassung als
aendggner-quelltext.tar.gz nebst Fassungskennung daneben und komprimiert nach
.gz und .br vor. Ein veränderter Arbeitsbaum bricht den Bau ab: Der beigelegte
Quelltext wäre alsdann nicht der ausgelieferte, und die Auflage des § 13 AGPLv3
wäre gerade verfehlt. Der Fuß verweist primär auf den Tarball, kompakt daneben
auf Gerrit als fortlaufende Quelle.
Die nginx-Vorlage wird vom server-Block zum location-Schnipsel. Vier Punkte sind
dabei mehr als Umschrift:
* Die Dateien werden über root statt über alias zugeordnet. In einer
Präfix-Location löst try_files gegen den root auf, nicht gegen den alias, und
lieferte sonst 404 für alles.
* Die Haltefrist von sieben Tagen entfällt zugunsten der Revalidierung. Die
Dateinamen tragen keine Fassungskennung; ein Wiederkehrer mit altem app.js und
neuem .wasm bekäme sonst eine Mischfassung, die es nie gegeben hat. Ein 304
für das unveränderte Modul kostet nichts.
* Vorkomprimiertes wird ausgeliefert (gzip_static/brotli_static), statt 24 MB je
Abruf neu zu packen — brotli drückt sie auf 7,1 MB.
* Hinzu treten die Sicherheitskopfzeilen samt Inhaltsrichtlinie. Zwei ihrer
Freigaben sind unvermeidlich, beide in der Vorlage begründet: 'wasm-unsafe-eval'
für die Instanziierung des Moduls (echtes eval kommt in der erzeugten
aendggner.js nicht vor), und 'unsafe-inline' für Stile, weil die Synopse als
blob:-Dokument die Richtlinie der erzeugenden Seite erbt, ihr Stylesheet aber
eingebettet trägt. Die Notwendigkeit ist gemessen, nicht vermutet: ohne die
Freigabe meldet das Synopse-Dokument null aktive Stylesheets und
Standardschrift, mit ihr eines mit dreiundzwanzig Regeln.
Dazu drei Kleinigkeiten, die erst unter einem Unterpfad auffallen: ein Hinweis
für abgeschaltetes JavaScript (bislang blieb das Formular wortlos wirkungslos),
ein relativ verwiesenes favicon.svg (der Abruf der Domainwurzel ginge ins Leere)
sowie Kurzbeschreibung und kanonische Adresse. Eine robots.txt unterbleibt
bewusst: Unter einem Unterpfad ist sie wirkungslos, maßgeblich ist allein die der
Domainwurzel; die Vorlage vermerkt, was dort einzutragen wäre.
Geprüft ist beides, Bau und Lauf: 304 Prüfungen laufen durch, -Pwasm package
hinterlässt ein Verzeichnis ohne .wat und mit vollständigem Tarball, und der
IfSG-Fall ergibt im Browser unter der Inhaltsrichtlinie 48 angewandte Befehle,
27 zur manuellen Prüfung und 21 geänderte Normen — dasselbe wie die
Befehlszeile, ohne eine einzige Beanstandung in der Konsole.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I004490ddaa7c424a60fb0ebac63fc9fc098736e3
diff --git a/FASSUNGEN.txt b/FASSUNGEN.txt
index ec8aeb5..ad304ba 100644
--- a/FASSUNGEN.txt
+++ b/FASSUNGEN.txt
@@ -21,6 +21,113 @@
════════════════════════════════════════════════════════════════════════════════
+ Fassung vom 16. August 2026,
+ zuletzt geändert durch die am 16. August 2026 vorgenommenen Änderungen
+════════════════════════════════════════════════════════════════════════════════
+
+Die Browserfassung war bislang für den öffentlichen Betrieb gesperrt: Die
+Anbieterkennzeichnung und die Datenschutzerklärung führten Platzhalter, und der
+Verweis auf den Quelltext, den die Lizenz des Erzeugnisses beim Netzbetrieb
+verlangt, zeigte auf keine Adresse. Mit dieser Fassung wird die Sperre
+aufgehoben; zugleich wird die Auslieferung auf den Betrieb unter einem Unterpfad
+einer bestehenden Domain eingerichtet.
+
+
+Artikel 1
+Anbieterkennzeichnung und Datenschutzerklärung
+
+(1) Die Anbieterkennzeichnung (impressum.html) wird mit den Angaben nach § 5 DDG
+ versehen; hinzu treten die Bezeichnung des nach § 18 Abs. 2 MStV
+ Verantwortlichen sowie ein Hinweis darauf, dass der Dienst unentgeltlich und
+ nichtkommerziell erbracht wird und die Synopsen ohne Gewähr ergehen.
+
+(2) Die Datenschutzerklärung (datenschutz.html) wird um die Bezeichnung des
+ Verantwortlichen (Art. 4 Nr. 7 DSGVO) und um die Rechte der betroffenen
+ Personen (Art. 15 bis 21, Art. 77 DSGVO) ergänzt. Für die Zugriffsprotokolle
+ werden die Rechtsgrundlage (Art. 6 Abs. 1 lit. f DSGVO) und die
+ Aufbewahrungsfrist (vierzehn Tage) benannt.
+
+(3) Die Aussage über die Dateien der Nutzer:innen bleibt unverändert; sie trifft
+ zu, weil die Verarbeitung ausschließlich im Browser stattfindet.
+
+
+Artikel 2
+Beilegung des Quelltextes
+
+(1) Es wird das Skript deploy/webpaket.sh eingeführt. Es tritt in der Phase
+ „package“ des Profils -Pwasm hinter den Übersetzer und macht aus dessen
+ Ergebnis ein auslieferbares Verzeichnis:
+ a) Der Textzwischenschritt (aendggner.js.wat) wird entfernt.
+ b) Der Quelltext der gebauten Fassung wird als aendggner-quelltext.tar.gz
+ nebst der Kennung der Fassung (quelltext-fassung.txt) beigelegt.
+ c) Die großen Dateien werden nach .gz und .br vorkomprimiert.
+
+(2) Trägt der Arbeitsbaum uneingecheckte Änderungen, so bricht der Bau ab. Der
+ Grund ist folgender: Der beigelegte Quelltext wäre alsdann nicht derjenige
+ der ausgelieferten Fassung, und die Auflage des § 13 AGPLv3 wäre verfehlt.
+ Für Probeläufe kann die Prüfung durch QUELLTEXT_UNGEPRUEFT=1 abbedungen
+ werden.
+
+(3) Der Fuß der Startseite (index.html) verweist fortan auf den beigelegten
+ Quelltext; die fortlaufende Quelle wird als Zweitverweis daneben genannt.
+
+
+Artikel 3
+Auslieferungsvorlage
+
+Die Vorlage (deploy/nginx-aendggner.conf) wird vom eigenen server-Block auf ein
+location-Schnipsel umgestellt, das in den bestehenden Server eingefügt wird. Sie
+wird wie folgt geändert:
+
+1. Aufgenommen wird die Weiterleitung des Pfades ohne abschließenden
+ Schrägstrich; ohne sie zielten sämtliche relativen Verweise der Seite auf die
+ Wurzel der Domain.
+
+2. Die Dateien werden über „root“ statt über „alias“ zugeordnet, weil try_files
+ in einer Präfix-Location gegen den root auflöst und mit alias fehlgeht.
+
+3. Die Haltefrist von sieben Tagen entfällt und wird durch Revalidierung
+ ersetzt. Der Grund ist folgender: Die Dateinamen tragen keine
+ Fassungskennung; ein Browser mit altem Steuerskript und neuem Rechenwerk
+ erhielte sonst eine Mischfassung, die es nie gegeben hat.
+
+4. Die vorkomprimierten Dateien werden ausgeliefert, statt das Rechenwerk je
+ Abruf neu zu packen.
+
+5. Aufgenommen werden die Sicherheitskopfzeilen nebst einer Inhaltsrichtlinie.
+ Zwei ihrer Freigaben sind unvermeidlich und in der Vorlage begründet:
+ diejenige für die Instanziierung des Moduls und diejenige für eingebettete
+ Stile, weil die Synopse als blob:-Dokument die Richtlinie der erzeugenden
+ Seite erbt, ihr Stylesheet aber eingebettet trägt.
+
+
+Artikel 4
+Ergänzungen der Oberfläche
+
+1. Die Startseite erhält einen Hinweis für den Fall abgeschalteten JavaScripts;
+ bislang blieb das Formular alsdann wortlos wirkungslos.
+
+2. Es wird ein Sinnbild (favicon.svg) eingeführt und in allen Seiten relativ
+ verwiesen; unter einem Unterpfad griffe der Abruf der Wurzel ins Leere.
+
+3. Die Startseite erhält eine Kurzbeschreibung und die Angabe ihrer kanonischen
+ Adresse; das Stilblatt wird um die Auszeichnung der Überschriften zweiter
+ Ordnung, der Anschriften und der Hinweise ergänzt.
+
+
+Schlussbestimmung
+
+Die vorstehenden Änderungen sind durch die Prüfung sämtlicher Testfälle
+(dreihundertvier an der Zahl) bestätigt worden. Die Inhaltsrichtlinie ist gegen
+die laufende Anwendung erprobt worden: Der Lauf des IfSG-Falles im Browser hat
+unter ihr achtundvierzig angewandte Befehle, siebenundzwanzig zur manuellen
+Prüfung und einundzwanzig geänderte Normen ergeben, mithin dasselbe wie die
+Befehlszeile, und keine Beanstandung hinterlassen; die Erforderlichkeit der
+Freigabe für eingebettete Stile ist dadurch belegt, dass die Synopse ohne sie
+unformatiert erscheint.
+
+
+════════════════════════════════════════════════════════════════════════════════
Fassung vom 15. August 2026,
zuletzt geändert durch die am 15. August 2026 vorgenommenen Änderungen
════════════════════════════════════════════════════════════════════════════════
diff --git a/README.adoc b/README.adoc
index 586b17f..b2329b7 100644
--- a/README.adoc
+++ b/README.adoc
@@ -325,9 +325,15 @@
----
Ergebnis ist `target/web/` mit `index.html`, `app.js`, `worker.js`,
-`style.css`, `aendggner.js` und `aendggner.js.wasm` (rund 20 MB, komprimiert
-etwa 6 MB). Die daneben liegende `aendggner.js.wat` ist ein Zwischenschritt des
-Übersetzers und gehört nicht auf den Server.
+`style.css`, `favicon.svg`, `aendggner.js` und `aendggner.js.wasm` (rund 24 MB,
+komprimiert etwa 7 MB). Das Verzeichnis ist so, wie es dasteht, auslieferbar:
+`deploy/webpaket.sh` läuft am Ende desselben Befehls, wirft den mehrere hundert
+Megabyte großen Textzwischenschritt `aendggner.js.wat` fort, legt den Quelltext
+der gebauten Fassung als `aendggner-quelltext.tar.gz` samt
+`quelltext-fassung.txt` bei und komprimiert die großen Dateien nach `.gz` und
+`.br` vor. Trägt der Arbeitsbaum uneingecheckte Änderungen, bricht der Bau ab —
+der beigelegte Quelltext wäre dann nicht der gebaute; für einen Probelauf hilft
+`QUELLTEXT_UNGEPRUEFT=1`.
Lokal ansehen — `file://` genügt nicht, Browser laden Wasm-Module und Worker
nur über HTTP (http://localhost:8000/):
@@ -337,11 +343,48 @@
jwebserver -d target/web
----
-Der öffentliche Betrieb braucht nur einen Webserver für statische Dateien;
-eine `nginx`-Vorlage liegt unter `deploy/nginx-aendggner.conf`. Die
-Befehlszeilenfassung bleibt davon unberührt und ist weiterhin der Weg für
+Die Befehlszeilenfassung bleibt davon unberührt und ist weiterhin der Weg für
Massenläufe.
+=== Ausrollen
+
+Betrieben wird die Fassung unter einem Unterpfad einer bestehenden Domain,
+https://matthias.benkard.de/aendggner/ — ein eigener Server und ein eigenes
+Zertifikat sind dafür nicht nötig. Nötig ist nur ein Webserver für statische
+Dateien.
+
+[source,shell script]
+----
+JAVA_HOME=/pfad/zu/oracle-graalvm ./mvnw -Pwasm package
+rsync -av --delete target/web/ server:/var/www/aendggner/
+----
+
+`deploy/nginx-aendggner.conf` ist dafür kein eigener `server`-Block, sondern ein
+Schnipsel zum Einfügen in den vorhandenen (`include`). Er bringt mit:
+
+* die Weiterleitung von `/aendggner` auf `/aendggner/`, ohne die alle relativen
+ Verweise der Seite auf die Domainwurzel zielten;
+* den MIME-Typ `application/wasm`, ohne den der Browser die Instanziierung des
+ Moduls verweigert;
+* `gzip_static`/`brotli_static` für die vorkomprimierten Dateien, statt 24 MB je
+ Abruf neu zu packen;
+* `Cache-Control: no-cache` statt einer Haltefrist: Die Dateinamen tragen keine
+ Fassungskennung, und ein Browser mit altem `app.js` und neuem `.wasm` bekäme
+ sonst eine Mischfassung, die es nie gegeben hat. Revalidiert wird per ETag, das
+ unveränderte Modul kostet dann ein 304 ohne Rumpf;
+* die Sicherheitskopfzeilen samt einer Content-Security-Policy. Zwei ihrer
+ Freigaben sind unvermeidlich und in der Datei begründet: `'wasm-unsafe-eval'`
+ für die Instanziierung des Moduls und `'unsafe-inline'` für Stile, weil die
+ Synopse als `blob:`-Dokument die Richtlinie der erzeugenden Seite erbt, ihr
+ Stylesheet aber eingebettet trägt.
+
+Der Footer der Startseite verweist auf den beigelegten Quelltext-Tarball; das
+verlangt AGPLv3 §13 für den Netzwerkbetrieb. Als fortlaufende Zweitquelle ist
+https://gerrit.benkard.de/plugins/gitiles/aendggner genannt. `impressum.html`
+und `datenschutz.html` tragen die Angaben nach § 5 DDG und Art. 13 DSGVO; die
+dort genannte Aufbewahrungsfrist der Zugriffsprotokolle (14 Tage) muss zu der
+des Servers passen.
+
=== Warum WebAssembly und nicht ein Java-Server
Der Kern ist reines Java ohne Dateisystem- oder Netzzugriff; nur vier Stellen
@@ -360,18 +403,6 @@
Dateiinhalt wandert deshalb als Base64-Text über die JS-Grenze.
-Vor dem produktiven, öffentlichen Betrieb sind zwingend zu erledigen:
-
-* `impressum.html` und `datenschutz.html`
- (`src/main/resources/eu/mulk/aendggner/web/`) mit den tatsächlich
- zutreffenden Angaben statt der `TODO`-Platzhalter füllen (Pflicht
- nach § 5 DDG bzw. DSGVO für öffentlich erreichbare Dienste).
-* Den Quellcode-Link im Footer von `index.html` auf das tatsächlich
- öffentlich erreichbare Repository setzen — das Projekt steht unter
- AGPLv3 (`COPYING`), dessen §13 bei Netzwerkbetrieb einen
- Quellcode-Zugriff für Nutzer:innen verlangt.
-
-
== Running the Tests
To build and run the tests:
diff --git a/deploy/nginx-aendggner.conf b/deploy/nginx-aendggner.conf
index 46eacb5..78677b5 100644
--- a/deploy/nginx-aendggner.conf
+++ b/deploy/nginx-aendggner.conf
@@ -1,47 +1,80 @@
-# Vorlage für die Auslieferung der ÄndGgner-Browserfassung. Es gibt keinen
-# Anwendungsprozess mehr: Der Server liefert nur statische Dateien aus
-# (Inhalt von target/web/), gerechnet wird im Browser.
+# Vorlage für die Auslieferung der ÄndGgner-Browserfassung unter
+# https://matthias.benkard.de/aendggner/.
#
-# Vor dem Einsatz anpassen: server_name, ssl_certificate(_key) (z. B. via certbot), root.
+# Es gibt keinen Anwendungsprozess: Der Server liefert nur statische Dateien aus
+# (Inhalt von target/web/), gerechnet wird im Browser. Weil der Dienst unter einem
+# Unterpfad einer bestehenden Domain läuft, ist dies kein eigener server-Block,
+# sondern ein Schnipsel zum Einfügen in den vorhandenen — Zertifikat, HTTP→HTTPS
+# und Domainname bleiben, wie sie sind:
+#
+# server { ... include /etc/nginx/snippets/aendggner.conf; ... }
+#
+# Ausgerollt wird nach /var/www/aendggner (siehe README.adoc, Abschnitt „Ausrollen“).
-server {
- listen 80;
- listen [::]:80;
- server_name aendggner.example.org;
- return 301 https://$host$request_uri;
+# Ohne diesen Schritt liefe /aendggner ins Leere, und alle relativen Verweise der
+# Seite gingen von / statt von /aendggner/ aus.
+location = /aendggner {
+ return 301 /aendggner/;
}
-server {
- listen 443 ssl http2;
- listen [::]:443 ssl http2;
- server_name aendggner.example.org;
-
- ssl_certificate /etc/letsencrypt/live/aendggner.example.org/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/aendggner.example.org/privkey.pem;
-
- # Inhalt von target/web/ — ohne aendggner.js.wat, das nur ein Zwischenschritt
- # des Übersetzers ist.
- root /var/www/aendggner;
+location /aendggner/ {
+ # Bewusst „root /var/www“ (mit dem Verzeichnis /var/www/aendggner) statt
+ # „alias /var/www/aendggner/“: In einer Präfix-Location löst try_files $uri
+ # gegen den root auf, nicht gegen den alias, und liefert dann 404 für alles.
+ root /var/www;
index index.html;
- # Ohne diesen Typ lehnt der Browser die Streaming-Instanziierung des Moduls ab.
+ try_files $uri $uri/ =404;
+
+ # Ohne den ersten Typ lehnt der Browser die Streaming-Instanziierung des Moduls
+ # ab. Ein types-Block in einer location ersetzt die Zuordnung vollständig, statt
+ # sie zu ergänzen — deshalb stehen hier alle vorkommenden Endungen.
types {
application/wasm wasm;
+ text/html html;
+ text/css css;
+ text/javascript js;
+ image/svg+xml svg;
+ text/plain txt;
+ application/gzip gz;
}
+ default_type application/octet-stream;
- gzip on;
- gzip_types application/wasm application/javascript text/css text/html;
- gzip_min_length 1024;
+ # Das Wasm-Modul ist rund 24 MB groß. deploy/webpaket.sh legt beim Bauen .gz
+ # und .br daneben; ausgeliefert wird die fertige Datei, statt sie je Abruf neu
+ # zu packen. gzip_static bringen die üblichen Distributionspakete mit;
+ # brotli_static setzt das Modul ngx_brotli voraus — fehlt es, die Zeile
+ # streichen, gzip_static genügt.
+ gzip_static on;
+ brotli_static on;
- # Das Wasm-Modul ist einige Megabyte groß und ändert sich nur mit einer neuen
- # Fassung; vorkomprimierte .gz/.br daneben zu legen lohnt sich (ngx_brotli bzw.
- # gzip_static).
- location ~ \.(wasm|js|css)$ {
- expires 7d;
- add_header Cache-Control "public";
- }
+ # Kein „expires 7d“: Die Dateinamen tragen keine Fassungskennung. Ein Browser
+ # mit altem app.js und neuem .wasm (oder umgekehrt) bekäme eine Mischfassung,
+ # die es nie gegeben hat. Stattdessen Revalidierung bei jedem Abruf; für das
+ # unveränderte Modul kostet das ein 304 ohne Rumpf.
+ add_header Cache-Control "no-cache" always;
- location / {
- try_files $uri $uri/ =404;
- }
+ # add_header in einer location verdrängt sämtliche vom server-Block geerbten
+ # Header. Alles, was hier gelten soll, muss deshalb hier stehen — auch ein
+ # dort gesetztes HSTS.
+ # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
+
+ # 'wasm-unsafe-eval' ist für die Instanziierung des Moduls nötig; echtes eval
+ # oder new Function kommt in der erzeugten aendggner.js nicht vor.
+ #
+ # style-src braucht 'unsafe-inline': Die fertige Synopse wird als blob:-Dokument
+ # geöffnet, und ein blob:-Dokument erbt die CSP des Dokuments, das es erzeugt
+ # hat. Der HtmlRenderer bettet sein Stylesheet als <style> ein — ohne die
+ # Freigabe erschiene die Synopse unformatiert. Skripte bleiben streng.
+ add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Referrer-Policy "no-referrer" always;
+ add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), usb=()" always;
}
+
+# Eine robots.txt unter /aendggner/ wäre wirkungslos: Maßgeblich ist allein die im
+# Wurzelverzeichnis der Domain. Wer die Seite von der Indizierung ausnehmen will,
+# trage dort ein:
+#
+# User-agent: *
+# Disallow: /aendggner/
diff --git a/deploy/webpaket.sh b/deploy/webpaket.sh
new file mode 100755
index 0000000..2003134
--- /dev/null
+++ b/deploy/webpaket.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# Macht aus dem Übersetzerergebnis ein auslieferbares Verzeichnis. Läuft in der Phase
+# „package“ des Profils -Pwasm, unmittelbar nach native-image.
+#
+# 1. Der Textzwischenschritt aendggner.js.wat (mehrere hundert Megabyte) wird entfernt.
+# 2. Der Quelltext der gebauten Fassung wird als Tarball beigelegt — AGPLv3 §13 verlangt
+# beim Netzwerkbetrieb einen Quellcode-Zugang für die Nutzer:innen, und der Footer der
+# Startseite verweist darauf.
+# 3. Die großen Dateien werden vorkomprimiert, damit nginx sie mit gzip_static bzw.
+# brotli_static ausliefern kann, statt 24 MB je Abruf neu zu packen.
+#
+# Aufruf: deploy/webpaket.sh <ausgabeverzeichnis> (Standard: target/web)
+
+set -eu
+
+ziel="${1:-target/web}"
+quelle="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)"
+
+if [ ! -d "$ziel" ]; then
+ echo "webpaket: $ziel gibt es nicht — erst -Pwasm package laufen lassen." >&2
+ exit 1
+fi
+
+# 1. Zwischenschritt fort.
+rm -f "$ziel/aendggner.js.wat"
+
+# 2. Quelltext der gebauten Fassung.
+#
+# Ein veränderter Arbeitsbaum bricht den Bau ab: Der angebotene Quelltext muss der
+# ausgelieferten Fassung entsprechen, sonst ist die Auflage aus §13 verfehlt. Wer nur
+# ausprobieren will, setzt QUELLTEXT_UNGEPRUEFT=1.
+if ! git -C "$quelle" rev-parse --git-dir >/dev/null 2>&1; then
+ echo "webpaket: $quelle ist kein Git-Arbeitsbaum; ohne ihn lässt sich der Quelltext" >&2
+ echo " der gebauten Fassung nicht beilegen (AGPLv3 §13)." >&2
+ exit 1
+fi
+
+if [ -n "$(git -C "$quelle" status --porcelain)" ] && [ "${QUELLTEXT_UNGEPRUEFT:-}" != 1 ]; then
+ echo "webpaket: Der Arbeitsbaum trägt uneingecheckte Änderungen. Der beigelegte Quelltext" >&2
+ echo " wäre dann nicht der gebaute. Erst einchecken — oder für einen Probelauf" >&2
+ echo " QUELLTEXT_UNGEPRUEFT=1 setzen (dann nicht ausliefern)." >&2
+ exit 1
+fi
+
+fassung="$(git -C "$quelle" rev-parse HEAD)"
+git -C "$quelle" archive --format=tar.gz --prefix=aendggner/ HEAD \
+ -o "$(CDPATH='' cd -- "$ziel" && pwd)/aendggner-quelltext.tar.gz"
+
+{
+ echo "ÄndGgner — Quelltext der ausgelieferten Fassung"
+ echo
+ echo "Commit: $fassung"
+ echo "Gebaut: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
+ echo
+ echo "Vollständig in aendggner-quelltext.tar.gz; Bauanleitung darin in README.adoc."
+ echo "Fortlaufend: https://gerrit.benkard.de/plugins/gitiles/aendggner"
+} > "$ziel/quelltext-fassung.txt"
+
+# 3. Vorkompression. Der Tarball ist bereits gepackt und bleibt außen vor.
+for datei in "$ziel"/*.wasm "$ziel"/*.js "$ziel"/*.css "$ziel"/*.html "$ziel"/*.svg \
+ "$ziel"/*.txt; do
+ [ -f "$datei" ] || continue
+ gzip -9 -f -k -- "$datei"
+ if command -v brotli >/dev/null 2>&1; then
+ brotli -f -- "$datei"
+ fi
+done
+
+if ! command -v brotli >/dev/null 2>&1; then
+ echo "webpaket: brotli nicht gefunden — nur .gz angelegt." >&2
+fi
+
+echo "webpaket: $ziel ist auslieferbar (Fassung $(git -C "$quelle" rev-parse --short HEAD))."
diff --git a/pom.xml b/pom.xml
index 601b9e9..b66267f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -392,6 +392,26 @@
</arguments>
</configuration>
</execution>
+
+ <!--
+ Macht aus dem Übersetzerergebnis ein auslieferbares Verzeichnis: entfernt den
+ .wat-Zwischenschritt, legt den Quelltext der gebauten Fassung bei (AGPLv3 §13)
+ und komprimiert vor. Läuft in derselben Phase, aber nach „web-image“ — Maven
+ führt gleichphasige Ausführungen in Deklarationsreihenfolge aus.
+ -->
+ <execution>
+ <id>webpaket</id>
+ <phase>package</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>${project.basedir}/deploy/webpaket.sh</executable>
+ <arguments>
+ <argument>${wasm.output.directory}</argument>
+ </arguments>
+ </configuration>
+ </execution>
</executions>
</plugin>
diff --git a/src/main/resources/eu/mulk/aendggner/web/datenschutz.html b/src/main/resources/eu/mulk/aendggner/web/datenschutz.html
index 5b9bbac..a8d046a 100644
--- a/src/main/resources/eu/mulk/aendggner/web/datenschutz.html
+++ b/src/main/resources/eu/mulk/aendggner/web/datenschutz.html
@@ -4,16 +4,24 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Datenschutz — ÄndGgner</title>
+ <link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Datenschutzerklärung</h1>
+
+ <h2>Verantwortlicher</h2>
<p>
- <strong>TODO vor dem Livegang:</strong> Bitte prüfen und um die für diesen konkreten Server
- zutreffenden Angaben ergänzen (Betreiber, Kontakt, tatsächliche Speicherdauer der
- Zugriffslogs, Hosting-Anbieter).
+ Verantwortlicher im Sinne des Art. 4 Nr. 7 DSGVO ist:
</p>
+ <address>
+ Matthias Benkard<br>
+ Machtlfinger Str. 29<br>
+ 81379 München<br>
+ Deutschland<br>
+ <a href="mailto:admin@mulk.eu">admin@mulk.eu</a>
+ </address>
<h2>Ihre Dateien</h2>
<p>
@@ -28,15 +36,36 @@
<h2>Zugriffsprotokolle</h2>
<p>
Beim Abruf der Seite selbst und der zugehörigen Dateien werden wie bei jedem Webserver
- technische Zugriffsdaten (u. a. IP-Adresse, Zeitpunkt, aufgerufene Adresse) für einen
- begrenzten Zeitraum zur Absicherung des Betriebs protokolliert. Diese Protokolle enthalten
- keine Angaben über die von Ihnen ausgewerteten Dokumente.
+ technische Zugriffsdaten (IP-Adresse, Zeitpunkt, aufgerufene Adresse, übertragene
+ Datenmenge, Statuscode sowie die vom Browser gemeldete Kennung und Verweisadresse)
+ protokolliert. Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO; das berechtigte Interesse
+ liegt in der Absicherung und Fehlersuche des Betriebs. Die Protokolle werden nach
+ <strong>14 Tagen</strong> gelöscht. Sie enthalten keine Angaben über die von Ihnen
+ ausgewerteten Dokumente, denn diese erreichen den Server nicht.
+ </p>
+ <p>
+ Der Server wird gegebenenfalls bei einem Dienstleister innerhalb der Europäischen Union
+ betrieben, der insoweit als Auftragsverarbeiter nach Art. 28 DSGVO tätig ist.
</p>
<h2>Keine Cookies, keine Konten, kein Tracking</h2>
<p>
Dieser Dienst verwendet keine Cookies, erfordert keine Registrierung und bindet keine
- Analyse- oder Tracking-Dienste Dritter ein.
+ Analyse- oder Tracking-Dienste Dritter ein. Es werden keine Daten an Dritte weitergegeben
+ und keine Inhalte von fremden Servern nachgeladen.
+ </p>
+
+ <h2>Ihre Rechte</h2>
+ <p>
+ Ihnen stehen gegenüber dem Verantwortlichen die Rechte auf Auskunft (Art. 15 DSGVO),
+ Berichtigung (Art. 16), Löschung (Art. 17), Einschränkung der Verarbeitung (Art. 18),
+ Datenübertragbarkeit (Art. 20) sowie das Recht auf Widerspruch gegen die Verarbeitung
+ (Art. 21) zu. Wenden Sie sich dazu an die oben genannte Anschrift oder E-Mail-Adresse.
+ </p>
+ <p>
+ Unabhängig davon können Sie sich bei einer Aufsichtsbehörde beschweren (Art. 77 DSGVO).
+ Zuständig ist hier das Bayerische Landesamt für Datenschutzaufsicht, Promenade 27,
+ 91522 Ansbach.
</p>
</main>
<footer><a href="index.html">Zurück zum Formular</a></footer>
diff --git a/src/main/resources/eu/mulk/aendggner/web/favicon.svg b/src/main/resources/eu/mulk/aendggner/web/favicon.svg
new file mode 100644
index 0000000..4f6a847
--- /dev/null
+++ b/src/main/resources/eu/mulk/aendggner/web/favicon.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="ÄndGgner">
+ <!-- Farben wie in style.css: heller Grund, brauner Akzent. Ein Favicon erbt keine
+ CSS-Variablen, deshalb stehen die Werte hier ausgeschrieben. -->
+ <rect width="32" height="32" rx="6" fill="#fdfdfb"/>
+ <rect x="0.75" y="0.75" width="30.5" height="30.5" rx="5.25" fill="none" stroke="#d8d3c4"
+ stroke-width="1.5"/>
+ <text x="16" y="17" fill="#6b4423" font-family="Georgia, 'Times New Roman', serif"
+ font-size="22" text-anchor="middle" dominant-baseline="central">§</text>
+</svg>
diff --git a/src/main/resources/eu/mulk/aendggner/web/impressum.html b/src/main/resources/eu/mulk/aendggner/web/impressum.html
index 525ef77..3832d03 100644
--- a/src/main/resources/eu/mulk/aendggner/web/impressum.html
+++ b/src/main/resources/eu/mulk/aendggner/web/impressum.html
@@ -4,22 +4,53 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Impressum — ÄndGgner</title>
+ <link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Impressum</h1>
- <p>
- <strong>TODO vor dem Livegang:</strong> Diese Seite muss durch die nach § 5 DDG
- (Digitale-Dienste-Gesetz, vormals § 5 TMG) vorgeschriebenen Angaben ersetzt werden, bevor
- dieser Dienst öffentlich betrieben wird.
- </p>
+
+ <h2>Angaben gemäß § 5 DDG</h2>
<address>
- [Name]<br>
- [Straße, Hausnummer]<br>
- [PLZ, Ort]<br>
- [E-Mail-Adresse]
+ Matthias Benkard<br>
+ Machtlfinger Str. 29<br>
+ 81379 München<br>
+ Deutschland
</address>
+
+ <h2>Kontakt</h2>
+ <p>
+ E-Mail: <a href="mailto:admin@mulk.eu">admin@mulk.eu</a><br>
+ Telefon: +49-8856-8054002<br>
+ Fax: +49-89-20008761207
+ </p>
+
+ <h2>Verantwortlich für den Inhalt nach § 18 Abs. 2 MStV</h2>
+ <address>
+ Matthias Benkard, Machtlfinger Str. 29, 81379 München
+ </address>
+
+ <h2>Zur Sache</h2>
+ <p>
+ ÄndGgner ist ein unentgeltlicher, nichtkommerzieller Dienst ohne Registrierung. Eine
+ Umsatzsteuer-Identifikationsnummer besteht nicht; eine berufsrechtliche Aufsicht besteht
+ nicht, da es sich weder um Rechtsberatung noch um eine sonstige reglementierte Tätigkeit
+ handelt.
+ </p>
+ <p>
+ Die erzeugten Synopsen entstehen selbsttätig und sind <strong>ohne Gewähr</strong> für
+ Vollständigkeit und Richtigkeit. Maßgeblich ist allein die amtliche Verkündung im
+ jeweiligen Gesetz- oder Verordnungsblatt.
+ </p>
+ <p>
+ Die zugrunde liegende Software steht unter der GNU Affero General Public License,
+ Version 3 (Wortlaut in der Datei <code>COPYING</code> des Quelltextes), und wird
+ ausdrücklich ohne jede Gewährleistung überlassen. Den
+ <a href="aendggner-quelltext.tar.gz">Quelltext der betriebenen Fassung</a> gibt es zum
+ Herunterladen; fortlaufend auch über
+ <a href="https://gerrit.benkard.de/plugins/gitiles/aendggner">Gerrit</a>.
+ </p>
</main>
<footer><a href="index.html">Zurück zum Formular</a></footer>
</body>
diff --git a/src/main/resources/eu/mulk/aendggner/web/index.html b/src/main/resources/eu/mulk/aendggner/web/index.html
index 9f26889..9d1e343 100644
--- a/src/main/resources/eu/mulk/aendggner/web/index.html
+++ b/src/main/resources/eu/mulk/aendggner/web/index.html
@@ -4,6 +4,11 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ÄndGgner — Änderungsgesetze konsolidieren</title>
+ <meta name="description" content="Wendet Änderungsgesetze und Gesetzentwürfe selbsttätig auf ein
+ Stammgesetz an und zeigt das Ergebnis als zweispaltige Synopse. Die Auswertung läuft
+ vollständig im Browser; die gewählten Dateien werden nicht übertragen.">
+ <link rel="canonical" href="https://matthias.benkard.de/aendggner/">
+ <link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="style.css">
<script src="app.js" defer></script>
</head>
@@ -16,6 +21,15 @@
</header>
<main>
+ <noscript>
+ <p class="noscript">
+ Dieses Werkzeug rechnet im Browser statt auf dem Server: Es braucht JavaScript und
+ WebAssembly. Ohne sie bleibt das nachstehende Formular wirkungslos. Wer beides nicht
+ einschalten möchte, verwende die Befehlszeilenfassung — sie liegt dem
+ <a href="aendggner-quelltext.tar.gz">Quelltext</a> bei und leistet dasselbe.
+ </p>
+ </noscript>
+
<form id="synopse-formular">
<div class="field">
<label for="stamm">Stammgesetz</label>
@@ -62,7 +76,8 @@
<footer>
<a href="impressum.html">Impressum</a> ·
<a href="datenschutz.html">Datenschutz</a> ·
- <a href="https://TODO-QUELLCODE-URL-EINTRAGEN">Quellcode (AGPLv3)</a>
+ <a href="aendggner-quelltext.tar.gz">Quelltext dieser Fassung (AGPLv3)</a>
+ <span class="nebenquelle">(<a href="https://gerrit.benkard.de/plugins/gitiles/aendggner">Git</a>)</span>
</footer>
</body>
</html>
diff --git a/src/main/resources/eu/mulk/aendggner/web/style.css b/src/main/resources/eu/mulk/aendggner/web/style.css
index cdea20e..8aa1e11 100644
--- a/src/main/resources/eu/mulk/aendggner/web/style.css
+++ b/src/main/resources/eu/mulk/aendggner/web/style.css
@@ -45,6 +45,21 @@
margin: 0;
}
+/* Impressum und Datenschutz. */
+h2 {
+ margin: 1.75rem 0 0.5rem;
+ font-size: 1.125rem;
+}
+
+address {
+ font-style: normal;
+ margin: 0.5rem 0;
+}
+
+a {
+ color: var(--accent);
+}
+
.field {
margin-bottom: 1.25rem;
}
@@ -92,6 +107,14 @@
filter: brightness(1.1);
}
+.noscript {
+ margin: 0 0 2rem;
+ padding: 1rem;
+ border: 1px solid #a33;
+ border-radius: 6px;
+ font-size: 0.875rem;
+}
+
.disclaimer {
margin-top: 2rem;
padding: 1rem;
@@ -113,6 +136,10 @@
color: var(--accent);
}
+.nebenquelle {
+ font-size: 0.8125rem;
+}
+
.meldung {
margin-top: 1.25rem;
padding: 0.75rem 1rem;