From b77ef80b5692721e18fc46cedaec60ad35267c07 Mon Sep 17 00:00:00 2001
From: Daniel Maksymilian Syrnicki
Date: Sat, 18 Apr 2026 12:10:06 +0200
Subject: [PATCH] feat(website): legal pages (Impressum/Datenschutz) +
auto-deploy on push-to-main
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Two coupled changes that make sense to land together:
1. Legal pages required under German law
- /imprint/ + /de/impressum/ — §5 DDG disclosure (contact is email
plus Forgejo-Issues as the second quick-contact channel, per ECJ
C-298/07 no phone number required)
- /privacy/ + /de/datenschutz/ — Art. 13 GDPR minimum: server-log
processing (IP, UA, URL, retention ≤30 days), no cookies, no
tracking, no third-party embeds. RLP Landesbeauftragter as the
competent supervisory authority.
- Footer partial linked from every page, localized per language.
- DE versions are legally binding; EN versions are courtesy
translations noting that.
2. Auto-deploy wired up
- New workflow .forgejo/workflows/deploy-site.yml fires on
push-to-main with paths under website/**. Runs on the self-hosted
runner, which *is* forge-runner-01 — so "deploy" is just a local
rsync into /srv/furtka-site and a hugo build into
/var/www/furtka.org. No SSH, no secrets.
- website/deploy-ci.sh is the SSH-free counterpart of deploy.sh,
invoked by the workflow.
- compose.yml bind-mounts /srv/furtka-site and /var/www/furtka.org
into the runner container at matching paths so the workflow can
reach them. Requires a one-time `docker compose up -d` on the
runner host to pick the mounts up.
- deploy.sh is kept for out-of-band manual deploys (testing from a
local branch, CI outage) but gets a header comment pointing at
the CI path as the normal flow.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
.forgejo/workflows/deploy-site.yml | 39 ++++++++++++++
ops/forgejo-runner/compose.yml | 7 +++
website/content/datenschutz.de.md | 79 +++++++++++++++++++++++++++
website/content/datenschutz.md | 80 ++++++++++++++++++++++++++++
website/content/impressum.de.md | 26 +++++++++
website/content/impressum.md | 29 ++++++++++
website/deploy-ci.sh | 27 ++++++++++
website/deploy.sh | 8 ++-
website/layouts/partials/footer.html | 7 +++
9 files changed, 301 insertions(+), 1 deletion(-)
create mode 100644 .forgejo/workflows/deploy-site.yml
create mode 100644 website/content/datenschutz.de.md
create mode 100644 website/content/datenschutz.md
create mode 100644 website/content/impressum.de.md
create mode 100644 website/content/impressum.md
create mode 100755 website/deploy-ci.sh
diff --git a/.forgejo/workflows/deploy-site.yml b/.forgejo/workflows/deploy-site.yml
new file mode 100644
index 0000000..a50b69d
--- /dev/null
+++ b/.forgejo/workflows/deploy-site.yml
@@ -0,0 +1,39 @@
+name: Deploy site
+
+# Auto-deploy the Hugo site to /var/www/furtka.org on push-to-main.
+# Only fires when content under website/ changes — everything else
+# (Python code, ISO build, runbook docs) is unaffected.
+#
+# Runs on the self-hosted runner, which is forge-runner-01 — the same
+# host that serves furtka.org. So the "deploy" is just a local rsync
+# of the Hugo source into /srv/furtka-site and a `hugo` build into
+# /var/www/furtka.org. No SSH, no secrets, no cross-host anything.
+#
+# Requires two bind-mounts on the runner container (/srv/furtka-site
+# and /var/www/furtka.org → same paths inside). See compose.yml.
+on:
+ push:
+ branches: [main]
+ paths:
+ - 'website/**'
+
+concurrency:
+ group: deploy-site
+ cancel-in-progress: true
+
+jobs:
+ deploy:
+ runs-on: self-hosted
+ timeout-minutes: 5
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install hugo + rsync
+ # Runner image is alpine-based; apk is fast and cached.
+ # Pinning is intentionally skipped — alpine:latest moves hugo
+ # forward in lockstep with upstream, and the site only uses
+ # baseline features.
+ run: apk add --no-cache hugo rsync
+
+ - name: Deploy
+ run: ./website/deploy-ci.sh
diff --git a/ops/forgejo-runner/compose.yml b/ops/forgejo-runner/compose.yml
index 7f33e71..76e71ba 100644
--- a/ops/forgejo-runner/compose.yml
+++ b/ops/forgejo-runner/compose.yml
@@ -19,6 +19,13 @@ services:
volumes:
- ./data:/data
- /var/run/docker.sock:/var/run/docker.sock
+ # Auto-deploy of furtka.org runs inside this container — the
+ # runner host *is* the web server. Bind these at matching paths
+ # so rsync/hugo just see plain local filesystem. Without these
+ # mounts, .forgejo/workflows/deploy-site.yml can't reach the
+ # source tree or the webroot.
+ - /srv/furtka-site:/srv/furtka-site
+ - /var/www/furtka.org:/var/www/furtka.org
command: >-
/bin/sh -c "apk add --no-cache nodejs docker-cli && sleep 5 &&
forgejo-runner daemon --config /data/config.yml"
diff --git a/website/content/datenschutz.de.md b/website/content/datenschutz.de.md
new file mode 100644
index 0000000..7de6dfd
--- /dev/null
+++ b/website/content/datenschutz.de.md
@@ -0,0 +1,79 @@
+---
+title: "Datenschutzerklärung"
+translationKey: "privacy"
+sitemap:
+ priority: 0.2
+---
+
+### Kurzfassung
+
+Diese Website setzt **keine Cookies**, lädt **keine Schriften oder
+Skripte von Drittanbietern**, bindet **keine Analyse- oder
+Tracking-Dienste** ein und enthält **keine externen Einbettungen**
+(YouTube, Maps, Social-Media-Buttons, …). Technisch anfallend sind
+ausschließlich kurzfristige Server-Zugriffsprotokolle.
+
+### Verantwortlicher
+
+
+Daniel Maksymilian Syrnicki
+Hauptstraße 35
+55569 Monzingen
+Deutschland
+E-Mail: hallo@furtka.org
+
+
+### Server-Logfiles
+
+Beim Aufruf der Website werden technisch notwendige Daten vom Browser an
+den Server übermittelt und dort in Logdateien gespeichert:
+
+- Datum und Uhrzeit des Aufrufs
+- IP-Adresse (in gekürzter Form)
+- aufgerufene URL
+- HTTP-Statuscode und übertragene Datenmenge
+- Referrer (falls vom Browser gesendet)
+- User-Agent-Kennung des Browsers
+
+**Zweck:** Auslieferung der Website und Abwehr missbräuchlicher Zugriffe.
+**Rechtsgrundlage:** Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse
+an Betrieb und Sicherheit).
+**Speicherdauer:** maximal 30 Tage, danach automatische Löschung.
+**Empfänger:** keine Weitergabe. Die Website läuft auf eigener
+Infrastruktur; es gibt keinen externen Auftragsverarbeiter.
+**Drittlandübermittlung:** keine.
+
+### Cookies und Tracking
+
+Keine. Es werden keine Cookies gesetzt, kein LocalStorage oder
+SessionStorage verwendet und keine Tracking- oder Analyse-Dienste
+eingebunden. Eine Einwilligung nach § 25 TDDDG ist daher nicht
+erforderlich.
+
+### Ihre Rechte
+
+Sie haben jederzeit das Recht auf:
+
+- Auskunft über die zu Ihrer Person gespeicherten Daten (Art. 15 DSGVO)
+- Berichtigung unrichtiger Daten (Art. 16 DSGVO)
+- Löschung (Art. 17 DSGVO)
+- Einschränkung der Verarbeitung (Art. 18 DSGVO)
+- Datenübertragbarkeit (Art. 20 DSGVO)
+- Widerspruch gegen die Verarbeitung (Art. 21 DSGVO)
+
+Anfragen hierzu richten Sie bitte per E-Mail an hallo@furtka.org.
+
+### Beschwerderecht
+
+Sie haben das Recht, sich bei einer Datenschutz-Aufsichtsbehörde zu
+beschweren. Zuständig ist:
+
+**Der Landesbeauftragte für den Datenschutz und die Informationsfreiheit
+Rheinland-Pfalz**
+Hintere Bleiche 34, 55116 Mainz
+Website:
+
+### Stand
+
+Diese Erklärung ist aktuell gültig und wurde zuletzt am 18.04.2026
+aktualisiert.
diff --git a/website/content/datenschutz.md b/website/content/datenschutz.md
new file mode 100644
index 0000000..4c88952
--- /dev/null
+++ b/website/content/datenschutz.md
@@ -0,0 +1,80 @@
+---
+title: "Privacy"
+translationKey: "privacy"
+url: /privacy/
+sitemap:
+ priority: 0.2
+---
+
+### In short
+
+This website sets **no cookies**, loads **no third-party fonts or
+scripts**, embeds **no analytics or tracking services**, and contains
+**no external embeds** (YouTube, Maps, social buttons, …). The only
+technical data collected is short-lived server access logs.
+
+### Controller
+
+
+Daniel Maksymilian Syrnicki
+Hauptstraße 35
+55569 Monzingen
+Germany
+Email: hallo@furtka.org
+
+
+### Server logs
+
+When you load a page, your browser sends technically necessary data to
+the server, which stores it in log files:
+
+- Date and time of the request
+- IP address (in shortened form)
+- Requested URL
+- HTTP status code and bytes transferred
+- Referrer (if sent by the browser)
+- User-agent string
+
+**Purpose:** serving the website and defending against abusive traffic.
+**Legal basis:** Art. 6(1)(f) GDPR — legitimate interest in operation
+and security.
+**Retention:** up to 30 days, then automatically deleted.
+**Recipients:** none. The site runs on our own infrastructure; no
+external processor is involved.
+**Transfers outside the EU/EEA:** none.
+
+### Cookies and tracking
+
+None. No cookies are set, no localStorage or sessionStorage is used, and
+no tracking or analytics services are embedded. Because of this, no
+consent under § 25 TDDDG (German implementation of the ePrivacy
+directive) is required.
+
+### Your rights
+
+You have the right at any time to:
+
+- Information about the data stored about you (Art. 15 GDPR)
+- Correction of inaccurate data (Art. 16 GDPR)
+- Erasure (Art. 17 GDPR)
+- Restriction of processing (Art. 18 GDPR)
+- Data portability (Art. 20 GDPR)
+- Object to processing (Art. 21 GDPR)
+
+Send requests to hallo@furtka.org.
+
+### Right to complain
+
+You have the right to file a complaint with a data protection
+supervisory authority. The competent one is:
+
+**Der Landesbeauftragte für den Datenschutz und die Informationsfreiheit
+Rheinland-Pfalz**
+Hintere Bleiche 34, 55116 Mainz, Germany
+Website:
+
+### Last updated
+
+This statement was last updated on 2026-04-18.
+
+The German version of this privacy statement is the legally binding one.
diff --git a/website/content/impressum.de.md b/website/content/impressum.de.md
new file mode 100644
index 0000000..4d17140
--- /dev/null
+++ b/website/content/impressum.de.md
@@ -0,0 +1,26 @@
+---
+title: "Impressum"
+translationKey: "imprint"
+sitemap:
+ priority: 0.2
+---
+
+Angaben gemäß § 5 DDG.
+
+
+Daniel Maksymilian Syrnicki
+Hauptstraße 35
+55569 Monzingen
+Deutschland
+
+
+### Kontakt
+
+- E-Mail: hallo@furtka.org
+- Forgejo-Issues:
+
+### Hinweis zum Projekt
+
+Furtka ist ein privates Open-Source-Projekt. Es ist kein Unternehmen,
+bietet keine kostenpflichtigen Leistungen an und nimmt keine Zahlungen
+entgegen. Der Quelltext steht unter der AGPL-3.0.
diff --git a/website/content/impressum.md b/website/content/impressum.md
new file mode 100644
index 0000000..1d9c50c
--- /dev/null
+++ b/website/content/impressum.md
@@ -0,0 +1,29 @@
+---
+title: "Imprint"
+translationKey: "imprint"
+url: /imprint/
+sitemap:
+ priority: 0.2
+---
+
+Information pursuant to § 5 DDG (German Digital Services Act).
+
+
+Daniel Maksymilian Syrnicki
+Hauptstraße 35
+55569 Monzingen
+Germany
+
+
+### Contact
+
+- Email: hallo@furtka.org
+- Forgejo issues:
+
+### Project note
+
+Furtka is a private open-source project. It is not a company, offers no
+paid services, and does not accept payments. The source code is licensed
+under AGPL-3.0.
+
+The German version of this imprint is the legally binding one.
diff --git a/website/deploy-ci.sh b/website/deploy-ci.sh
new file mode 100755
index 0000000..454622b
--- /dev/null
+++ b/website/deploy-ci.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+# Auto-deploy path run by .forgejo/workflows/deploy-site.yml inside the
+# self-hosted runner — which is forge-runner-01, the actual web server.
+# Same effect as deploy.sh but without the SSH hop: everything is local.
+#
+# Requires `rsync` and `hugo` on PATH. The workflow apk-installs both
+# before invoking this script.
+set -euo pipefail
+
+HERE="$(cd "$(dirname "$0")" && pwd)"
+SRCROOT="${FURTKA_SRCROOT:-/srv/furtka-site}"
+WEBROOT="${FURTKA_WEBROOT:-/var/www/furtka.org}"
+
+echo "==> rsync website/ → $SRCROOT"
+rsync -az --delete \
+ --exclude='.hugo_build.lock' \
+ --exclude='public/' \
+ --exclude='resources/' \
+ --exclude='deploy.sh' \
+ --exclude='deploy-ci.sh' \
+ "$HERE/" "$SRCROOT/"
+
+echo "==> hugo build → $WEBROOT"
+cd "$SRCROOT"
+hugo --minify --cleanDestinationDir -d "$WEBROOT"
+
+echo "OK: deployed to https://furtka.org/"
diff --git a/website/deploy.sh b/website/deploy.sh
index 04a5cea..9ef1b17 100755
--- a/website/deploy.sh
+++ b/website/deploy.sh
@@ -1,6 +1,12 @@
#!/usr/bin/env bash
-# Deploy furtka.org to forge-runner-01.
+# Manual deploy of furtka.org to forge-runner-01.
# Rsyncs the Hugo source up to the VM and builds it in-place.
+#
+# Normal path is now the `Deploy site` Forgejo workflow, which auto-fires
+# on every push-to-main that touches website/** — see
+# .forgejo/workflows/deploy-site.yml. Keep this script for out-of-band
+# pushes (testing from a local branch, recovering from a CI outage,
+# whatever). The in-CI equivalent that skips the SSH hop is deploy-ci.sh.
set -euo pipefail
HOST="${FURTKA_HOST:-forge-runner}"
diff --git a/website/layouts/partials/footer.html b/website/layouts/partials/footer.html
index 48af58b..4b62096 100644
--- a/website/layouts/partials/footer.html
+++ b/website/layouts/partials/footer.html
@@ -5,5 +5,12 @@
· AGPL-3.0 ·
{{ site.Params.contactEmail }}
+
+ {{ if eq site.Language.Lang "de" -}}
+ Impressum · Datenschutz
+ {{- else -}}
+ Imprint · Privacy
+ {{- end }}
+