Compare commits

..
2 Commits
Author SHA1 Message Date
irrlichtandClaude Sonnet 5 8a1ab8a146 Poll-Takt von 15 Minuten auf 2 Stunden senken
Reduziert den Traffic gegen die Quellen. slot_von() rundet jetzt generisch
auf TAKT-Grenzen statt fest auf 15-Minuten-Slots, die Stillstands-Schwelle
in quellen_stillstand steigt von 6h auf 24h, damit der Watchdog nicht schon
nach drei verpassten Slots anschlaegt. GDELT-Vergleich aus den Kommentaren
entfernt, da die Slots nicht mehr mit dessen 15-Minuten-Paketen zusammenfallen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vHZCwcCJnz4LKNgvT4bxv
2026-09-04 20:34:22 +02:00
irrlichtandClaude Sonnet 5 c43d8496bf Branding auf Wurzelwerk (SWP) umstellen, Deployment systemagnostisch machen
Ersetzt "Lagebild" durch den Projektnamen Wurzelwerk (Stinkwurzpresse) in
allen Units, Configs und Docs. Entfernt konkrete Hostnamen (Bebop, Pi 5,
Debby) aus README/NOTES zugunsten generischer Rollenbezeichnungen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vHZCwcCJnz4LKNgvT4bxv
2026-09-04 20:28:57 +02:00
12 changed files with 85 additions and 77 deletions
+2
View File
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
+1 -1
View File
@@ -9,6 +9,6 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY poller.py sources.toml ./ COPY poller.py sources.toml ./
USER ingest USER ingest
# Ohne Argumente: Dauerbetrieb im 15-Minuten-Takt, Senke aus $DATABASE_URL. # Ohne Argumente: Dauerbetrieb im 2-Stunden-Takt, Senke aus $DATABASE_URL.
# -u, damit die Logs unmittelbar im journal landen und nicht im Puffer haengen. # -u, damit die Logs unmittelbar im journal landen und nicht im Puffer haengen.
ENTRYPOINT ["python", "-u", "/app/poller.py"] ENTRYPOINT ["python", "-u", "/app/poller.py"]
+22 -22
View File
@@ -1,4 +1,4 @@
# Ingest # Wurzelwerk — Ingest
Stufe 0 und 1 der Pipeline: Quellenregister und Roh-Erfassung. Keine Stufe 0 und 1 der Pipeline: Quellenregister und Roh-Erfassung. Keine
Kodierung — was hier landet, soll sich beliebig oft neu kodieren lassen. Kodierung — was hier landet, soll sich beliebig oft neu kodieren lassen.
@@ -10,54 +10,54 @@ Kodierung — was hier landet, soll sich beliebig oft neu kodieren lassen.
| `sources.toml` | Quellenregister, einzige Wahrheit ueber die Quellen | | `sources.toml` | Quellenregister, einzige Wahrheit ueber die Quellen |
| `schema.sql` | Postgres-Schema (`quellen`, `raw_items`, `codings`, `poll_laeufe`) | | `schema.sql` | Postgres-Schema (`quellen`, `raw_items`, `codings`, `poll_laeufe`) |
| `poller.py` | Poller, zwei Senken: Postgres und gzip-JSONL | | `poller.py` | Poller, zwei Senken: Postgres und gzip-JSONL |
| `Containerfile` | Image fuer Bebop | | `Containerfile` | Image fuer den Produktivbetrieb |
| `lagebild-ingest.container`, `lagebild.network` | Quadlet-Units, rootless | | `wurzelwerk-ingest.container`, `wurzelwerk.network` | Quadlet-Units, rootless |
| `ingest.env.example` | Vorlage fuer die Zugangsdaten | | `ingest.env.example` | Vorlage fuer die Zugangsdaten |
## Bebop ## Deploy mit Postgres + Podman/Quadlet (rootless)
```sh ```sh
# 1. Schema einspielen # 1. Schema einspielen
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f schema.sql psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f schema.sql
# 2. Image bauen # 2. Image bauen
podman build -t localhost/lagebild-ingest:latest . podman build -t localhost/wurzelwerk-ingest:latest .
# 3. Konfiguration ablegen # 3. Konfiguration ablegen
mkdir -p ~/.config/lagebild mkdir -p ~/.config/wurzelwerk
cp ingest.env.example ~/.config/lagebild/ingest.env cp ingest.env.example ~/.config/wurzelwerk/ingest.env
cp sources.toml ~/.config/lagebild/sources.toml cp sources.toml ~/.config/wurzelwerk/sources.toml
chmod 600 ~/.config/lagebild/ingest.env chmod 600 ~/.config/wurzelwerk/ingest.env
$EDITOR ~/.config/lagebild/ingest.env $EDITOR ~/.config/wurzelwerk/ingest.env
# 4. Quadlet installieren # 4. Quadlet installieren
mkdir -p ~/.config/containers/systemd mkdir -p ~/.config/containers/systemd
cp lagebild-ingest.container lagebild.network ~/.config/containers/systemd/ cp wurzelwerk-ingest.container wurzelwerk.network ~/.config/containers/systemd/
systemctl --user daemon-reload systemctl --user daemon-reload
systemctl --user start lagebild-ingest systemctl --user start wurzelwerk-ingest
journalctl --user -u lagebild-ingest -f journalctl --user -u wurzelwerk-ingest -f
# Damit der Dienst ohne offene Sitzung weiterlaeuft: # Damit der Dienst ohne offene Sitzung weiterlaeuft:
loginctl enable-linger "$USER" loginctl enable-linger "$USER"
``` ```
`sources.toml` ist eingehaengt, nicht ins Image gebacken: eine neue Quelle ist `sources.toml` ist eingehaengt, nicht ins Image gebacken: eine neue Quelle ist
ein `systemctl --user restart lagebild-ingest`, kein Neubau. ein `systemctl --user restart wurzelwerk-ingest`, kein Neubau.
## Pi 5 — Schatten-Ingest ## Schatten-Ingest auf einem Zweitgeraet
Ohne Postgres, ohne Container: Ohne Postgres, ohne Container:
```sh ```sh
python3 poller.py --sink jsonl:/var/lib/lagebild/schatten python3 poller.py --sink jsonl:/pfad/zu/deinem/ablageordner
``` ```
Legt `<dir>/YYYYMMDD/YYYYMMDDHHMMSS.jsonl.gz` an, dedupliziert ueber eine Legt `<dir>/YYYYMMDD/YYYYMMDDHHMMSS.jsonl.gz` an, dedupliziert ueber eine
SQLite daneben. Als `systemd --user`-Unit mit `Restart=always` einrichten. SQLite daneben. Als `systemd --user`-Unit mit `Restart=always` einrichten.
Der Sinn ist nicht Redundanz um ihrer selbst willen: RSS-Feeds haben kein Der Sinn ist nicht Redundanz um ihrer selbst willen: RSS-Feeds haben kein
Archiv. Steht Bebops Poller einen Tag still, ist dieser Tag unwiederbringlich Archiv. Steht der Produktiv-Poller einen Tag still, ist dieser Tag
weg. Kodierungen lassen sich wiederholen, Rohdaten nie. unwiederbringlich weg. Kodierungen lassen sich wiederholen, Rohdaten nie.
## Betrieb ## Betrieb
@@ -101,8 +101,8 @@ ueber die gesamte Historie, ohne dass ein Feed erneut abgerufen wird.
## Verhalten ## Verhalten
- **15-Minuten-Takt**, an den Slotgrenzen ausgerichtet (+30 s Versatz), - **2-Stunden-Takt**, an den Slotgrenzen ausgerichtet (+30 s Versatz), um
identisch zu GDELTs Paketgrenzen — damit bleiben die Slots vergleichbar. unnoetigen Traffic bei den Quellen zu vermeiden.
- **Bedingter GET** ueber ETag/Last-Modified, im Register gespeichert. Im - **Bedingter GET** ueber ETag/Last-Modified, im Register gespeichert. Im
Test antworteten 4 von 12 Quellen im zweiten Durchlauf mit 304. Test antworteten 4 von 12 Quellen im zweiten Durchlauf mit 304.
- **Dedup** ueber `unique (quelle, item_key)`; `item_key` ist guid, ersatzweise - **Dedup** ueber `unique (quelle, item_key)`; `item_key` ist guid, ersatzweise
@@ -130,5 +130,5 @@ spiegel | derstandard | 0.92 | Niederlage fuer Donald Trump - Gericht in Missou
- Telegram-Fetcher (Telethon/MTProto). Register ist vorbereitet, Eintraege - Telegram-Fetcher (Telethon/MTProto). Register ist vorbereitet, Eintraege
stehen auf `aktiv = false`. Die Bot-API kann fremde Kanaele nicht lesen. stehen auf `aktiv = false`. Die Bot-API kann fremde Kanaele nicht lesen.
- Ersatz-URL fuer den BR24-Feed (aktuell 404). - Ersatz-URL fuer den BR24-Feed (aktuell 404).
- Quellenpool von 12 auf ~40 erweitern; Regionalzeitungen ueber SearXNG auf - Quellenpool von 12 auf ~40 erweitern; Regionalzeitungen ueber SearXNG
Debby suchen. suchen.
+22 -18
View File
@@ -1,44 +1,48 @@
# swp-01-ingest # swp-01-ingest — Wurzelwerk
Stufe 0/1 der Lagebild-Pipeline: Quellenregister und Roh-Erfassung Stufe 0/1 der Wurzelwerk-Pipeline (Stinkwurzpresse, SWP): Quellenregister
(RSS-Poller, Senken Postgres oder gzip-JSONL). Details zu Betrieb, und Roh-Erfassung (RSS-Poller, Senken Postgres oder gzip-JSONL). Details zu
Verhalten und offenen Punkten stehen in [`NOTES.md`](NOTES.md). Betrieb, Verhalten und offenen Punkten stehen in [`NOTES.md`](NOTES.md).
## Deploy (Bebop, Postgres + Podman/Quadlet) ## Deploy (Server mit Postgres + Podman/Quadlet)
```sh ```sh
# 1. Schema einspielen # 1. Schema einspielen
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f schema.sql psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f schema.sql
# 2. Image bauen # 2. Image bauen
podman build -t localhost/lagebild-ingest:latest . podman build -t localhost/wurzelwerk-ingest:latest .
# 3. Konfiguration ablegen # 3. Konfiguration ablegen
mkdir -p ~/.config/lagebild mkdir -p ~/.config/wurzelwerk
cp ingest.env.example ~/.config/lagebild/ingest.env cp ingest.env.example ~/.config/wurzelwerk/ingest.env
cp sources.toml ~/.config/lagebild/sources.toml cp sources.toml ~/.config/wurzelwerk/sources.toml
chmod 600 ~/.config/lagebild/ingest.env chmod 600 ~/.config/wurzelwerk/ingest.env
$EDITOR ~/.config/lagebild/ingest.env $EDITOR ~/.config/wurzelwerk/ingest.env
# 4. Quadlet installieren # 4. Quadlet installieren
mkdir -p ~/.config/containers/systemd mkdir -p ~/.config/containers/systemd
cp lagebild-ingest.container lagebild.network ~/.config/containers/systemd/ cp wurzelwerk-ingest.container wurzelwerk.network ~/.config/containers/systemd/
systemctl --user daemon-reload systemctl --user daemon-reload
systemctl --user start lagebild-ingest systemctl --user start wurzelwerk-ingest
journalctl --user -u lagebild-ingest -f journalctl --user -u wurzelwerk-ingest -f
# Damit der Dienst ohne offene Sitzung weiterlaeuft: # Damit der Dienst ohne offene Sitzung weiterlaeuft:
loginctl enable-linger "$USER" loginctl enable-linger "$USER"
``` ```
`sources.toml` ist eingehaengt, nicht ins Image gebacken: eine neue Quelle `sources.toml` ist eingehaengt, nicht ins Image gebacken: eine neue Quelle
ist ein `systemctl --user restart lagebild-ingest`, kein Neubau. ist ein `systemctl --user restart wurzelwerk-ingest`, kein Neubau.
## Deploy (Pi 5, ohne Postgres/Container) ## Deploy (Zweitgeraet ohne Postgres/Container, z. B. als Schatten-Ingest)
Fuer ein schlankes Zweitsystem ohne Postgres und ohne Container genuegt
Python 3.11+ mit den Paketen aus `requirements.txt`:
```sh ```sh
python3 poller.py --sink jsonl:/var/lib/lagebild/schatten python3 poller.py --sink jsonl:/pfad/zu/deinem/ablageordner
``` ```
Als `systemd --user`-Unit mit `Restart=always` einrichten (siehe Als `systemd --user`-Unit mit `Restart=always` einrichten (siehe
`lagebild-robots.service` / `.timer` fuer den woechentlichen Robots-Check). `wurzelwerk-robots.service` / `.timer` fuer den woechentlichen
Robots-Check).
+3 -3
View File
@@ -1,10 +1,10 @@
# Nach ~/.config/lagebild/ingest.env kopieren und ausfuellen (chmod 600). # Nach ~/.config/wurzelwerk/ingest.env kopieren und ausfuellen (chmod 600).
# #
# Bei rootless Podman erreichen sich Container ueber ein gemeinsames Netz per # Bei rootless Podman erreichen sich Container ueber ein gemeinsames Netz per
# Containername. Liegt Postgres bereits in einem Netz, dort denselben Namen # Containername. Liegt Postgres bereits in einem Netz, dort denselben Namen
# eintragen wie in der Zeile Network= der Quadlet-Datei. # eintragen wie in der Zeile Network= der Quadlet-Datei.
DATABASE_URL=postgresql://lagebild:GEHEIM@lagebild-pg:5432/lagebild DATABASE_URL=postgresql://wurzelwerk:GEHEIM@wurzelwerk-pg:5432/wurzelwerk
# Kontaktadresse gehoert in den User-Agent: wer unsere Abrufe sieht, soll # Kontaktadresse gehoert in den User-Agent: wer unsere Abrufe sieht, soll
# wissen, wen er anschreiben kann. # wissen, wen er anschreiben kann.
INGEST_USER_AGENT=lagebild-ingest/0.1 (+https://DEINE-DOMAIN/lagebild; kontakt@DEINE-DOMAIN) INGEST_USER_AGENT=wurzelwerk-ingest/0.1 (+https://stinkwurzpresse.de/wurzelwerk; kontakt@stinkwurzpresse.de)
-6
View File
@@ -1,6 +0,0 @@
# Ablegen unter ~/.config/containers/systemd/lagebild.network
[Unit]
Description=Lagebild internes Netz
[Network]
NetworkName=lagebild
+11 -10
View File
@@ -1,16 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Ingest-Poller fuer den deutschen Ereignisdatensatz. """Ingest-Poller fuer den deutschen Ereignisdatensatz.
Liest die Quellen aus sources.toml, holt alle 15 Minuten die Feeds und legt Liest die Quellen aus sources.toml, holt alle 2 Stunden die Feeds und legt
jedes neue Item roh ab. Bewusst ohne Kodierung: was hier landet, soll sich jedes neue Item roh ab. Bewusst ohne Kodierung: was hier landet, soll sich
beliebig oft neu kodieren lassen. beliebig oft neu kodieren lassen.
Zwei Senken: Zwei Senken:
--sink postgres://... Bebop, die eigentliche Datenbank --sink postgres://... Produktivbetrieb, die eigentliche Datenbank
--sink jsonl:/pfad/dir Pi 5, Schatten-Ingest als gzip-JSONL pro Slot --sink jsonl:/pfad/dir Schatten-Ingest als gzip-JSONL pro Slot
Aufrufe: Aufrufe:
poller.py --sink "$DATABASE_URL" # Dauerbetrieb, 15-Minuten-Takt poller.py --sink "$DATABASE_URL" # Dauerbetrieb, 2-Stunden-Takt
poller.py --sink "$DATABASE_URL" --once # ein Durchlauf poller.py --sink "$DATABASE_URL" --once # ein Durchlauf
poller.py --check-robots # Lizenzstatus nachpruefen poller.py --check-robots # Lizenzstatus nachpruefen
""" """
@@ -38,9 +38,9 @@ from email.utils import parsedate_to_datetime
HIER = pathlib.Path(__file__).parent HIER = pathlib.Path(__file__).parent
USER_AGENT = os.environ.get( USER_AGENT = os.environ.get(
"INGEST_USER_AGENT", "INGEST_USER_AGENT",
"lagebild-ingest/0.1 (+https://example.org/lagebild; kontakt@example.org)", "wurzelwerk-ingest/0.1 (+https://stinkwurzpresse.de/wurzelwerk; kontakt@stinkwurzpresse.de)",
) )
TAKT = 15 * 60 # Slotlaenge in Sekunden, wie bei GDELT TAKT = 2 * 60 * 60 # Slotlaenge in Sekunden
VERSATZ = 30 # Sekunden nach der Slotgrenze, nicht exakt darauf VERSATZ = 30 # Sekunden nach der Slotgrenze, nicht exakt darauf
NS = { NS = {
"a": "http://www.w3.org/2005/Atom", "a": "http://www.w3.org/2005/Atom",
@@ -66,9 +66,10 @@ def _jetzt():
def slot_von(ts): def slot_von(ts):
"""Auf 15 Minuten abrunden, wie GDELTs Paketgrenzen.""" """Auf TAKT-Sekunden seit Epoch abrunden (bei 2 h also volle gerade Stunden)."""
return ts.replace(second=0, microsecond=0, sekunden = int(ts.timestamp())
minute=(ts.minute // 15) * 15) return dt.datetime.fromtimestamp(
(sekunden // TAKT) * TAKT, tz=dt.timezone.utc)
def norm_titel(t): def norm_titel(t):
@@ -184,7 +185,7 @@ class JsonlSenke:
"""Schatten-Ingest fuer den Pi: gzip-JSONL, ein Verzeichnis pro Tag. """Schatten-Ingest fuer den Pi: gzip-JSONL, ein Verzeichnis pro Tag.
Kein Schema, keine Abfragen - nur die Garantie, dass die Rohdaten noch Kein Schema, keine Abfragen - nur die Garantie, dass die Rohdaten noch
da sind, wenn Bebop einen Tag lang stillstand. Die SQLite daneben haelt da sind, wenn der Produktiv-Poller einen Tag lang stillstand. Die SQLite daneben haelt
nur die gesehenen Schluessel, damit nicht bei jedem Slot der komplette nur die gesehenen Schluessel, damit nicht bei jedem Slot der komplette
Feed erneut geschrieben wird. Feed erneut geschrieben wird.
""" """
+4 -3
View File
@@ -48,7 +48,7 @@ create table if not exists raw_items (
autor text, autor text,
pubdate timestamptz, -- Angabe der Quelle, kann fehlen/luegen pubdate timestamptz, -- Angabe der Quelle, kann fehlen/luegen
abgerufen_am timestamptz not null default now(), abgerufen_am timestamptz not null default now(),
slot timestamptz not null, -- auf 15 min abgerundet, GDELT-kompatibel slot timestamptz not null, -- auf den Poll-Takt abgerundet
sprache text not null default 'de', sprache text not null default 'de',
roh jsonb not null, -- alle geparsten Feldwerte, unveraendert roh jsonb not null, -- alle geparsten Feldwerte, unveraendert
@@ -113,7 +113,8 @@ create table if not exists poll_laeufe (
create index if not exists poll_laeufe_slot_idx on poll_laeufe (slot desc); create index if not exists poll_laeufe_slot_idx on poll_laeufe (slot desc);
create index if not exists poll_laeufe_quelle_idx on poll_laeufe (quelle, begonnen_am desc); create index if not exists poll_laeufe_quelle_idx on poll_laeufe (quelle, begonnen_am desc);
-- Welche aktive Quelle hat seit ueber sechs Stunden nichts Neues geliefert? -- Welche aktive Quelle hat seit ueber 24 Stunden (12 verpasste Slots bei
-- 2-Stunden-Takt) nichts Neues geliefert?
create or replace view quellen_stillstand as create or replace view quellen_stillstand as
select q.id, q.name, max(r.abgerufen_am) as letztes_item, select q.id, q.name, max(r.abgerufen_am) as letztes_item,
now() - max(r.abgerufen_am) as stille now() - max(r.abgerufen_am) as stille
@@ -122,5 +123,5 @@ left join raw_items r on r.quelle = q.id
where q.aktiv where q.aktiv
group by q.id, q.name group by q.id, q.name
having max(r.abgerufen_am) is null having max(r.abgerufen_am) is null
or now() - max(r.abgerufen_am) > interval '6 hours' or now() - max(r.abgerufen_am) > interval '24 hours'
order by stille desc nulls first; order by stille desc nulls first;
@@ -1,29 +1,29 @@
# Quadlet-Unit fuer rootless Podman. # Quadlet-Unit fuer rootless Podman.
# Ablegen unter ~/.config/containers/systemd/lagebild-ingest.container, # Ablegen unter ~/.config/containers/systemd/wurzelwerk-ingest.container,
# danach: systemctl --user daemon-reload && systemctl --user start lagebild-ingest # danach: systemctl --user daemon-reload && systemctl --user start wurzelwerk-ingest
# #
# Der Poller laeuft dauerhaft und taktet sich selbst auf die 15-Minuten-Grenzen. # Der Poller laeuft dauerhaft und taktet sich selbst auf die 2-Stunden-Grenzen.
# Deshalb kein systemd-Timer: ein Timer wuerde den bedingten GET-Zustand # Deshalb kein systemd-Timer: ein Timer wuerde den bedingten GET-Zustand
# (ETag/Last-Modified) bei jedem Start neu aufbauen muessen. # (ETag/Last-Modified) bei jedem Start neu aufbauen muessen.
[Unit] [Unit]
Description=Lagebild Ingest-Poller Description=Wurzelwerk Ingest-Poller
After=network-online.target After=network-online.target
Wants=network-online.target Wants=network-online.target
[Container] [Container]
Image=localhost/lagebild-ingest:latest Image=localhost/wurzelwerk-ingest:latest
ContainerName=lagebild-ingest ContainerName=wurzelwerk-ingest
AutoUpdate=local AutoUpdate=local
EnvironmentFile=%h/.config/lagebild/ingest.env EnvironmentFile=%h/.config/wurzelwerk/ingest.env
# Quellenregister von aussen einhaengen: eine neue Quelle ist dann ein # Quellenregister von aussen einhaengen: eine neue Quelle ist dann ein
# Neustart, kein Neubau des Images. # Neustart, kein Neubau des Images.
Volume=%h/.config/lagebild/sources.toml:/app/sources.toml:ro,Z Volume=%h/.config/wurzelwerk/sources.toml:/app/sources.toml:ro,Z
# Dasselbe Netz wie der Postgres-Container. # Dasselbe Netz wie der Postgres-Container.
Network=lagebild.network Network=wurzelwerk.network
NoNewPrivileges=true NoNewPrivileges=true
ReadOnly=true ReadOnly=true
@@ -3,11 +3,11 @@
# damit systemd den Fehlerzustand sichtbar haelt. # damit systemd den Fehlerzustand sichtbar haelt.
[Unit] [Unit]
Description=Lagebild - robots.txt der Quellen pruefen Description=Wurzelwerk - robots.txt der Quellen pruefen
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=/usr/bin/podman run --rm \ ExecStart=/usr/bin/podman run --rm \
--network=host \ --network=host \
-v %h/.config/lagebild/sources.toml:/app/sources.toml:ro \ -v %h/.config/wurzelwerk/sources.toml:/app/sources.toml:ro \
localhost/lagebild-ingest:latest --check-robots localhost/wurzelwerk-ingest:latest --check-robots
@@ -1,8 +1,8 @@
# Nach ~/.config/systemd/user/ ablegen, dann: # Nach ~/.config/systemd/user/ ablegen, dann:
# systemctl --user enable --now lagebild-robots.timer # systemctl --user enable --now wurzelwerk-robots.timer
[Unit] [Unit]
Description=Lagebild - woechentliche robots.txt-Pruefung Description=Wurzelwerk - woechentliche robots.txt-Pruefung
[Timer] [Timer]
OnCalendar=Mon 05:30 OnCalendar=Mon 05:30
+6
View File
@@ -0,0 +1,6 @@
# Ablegen unter ~/.config/containers/systemd/wurzelwerk.network
[Unit]
Description=Wurzelwerk internes Netz
[Network]
NetworkName=wurzelwerk