457 lines
18 KiB
Markdown
457 lines
18 KiB
Markdown
# Gardomatic
|
||
|
||
Gardomatic ist eine mobile, mehrbenutzerfähige Webanwendung zur gemeinsamen
|
||
Organisation von Gärten. Sie bündelt Pflanzenwissen, Pflanzorte, anstehende
|
||
Arbeiten, Bilder und ein Gartentagebuch in einer Anwendung.
|
||
|
||
Das Projekt besteht aus einer JSON-API und einer serverseitig gerenderten
|
||
Webanwendung in Go. PostgreSQL speichert alle Fachdaten. Das Frontend ist
|
||
mobile-first, bleibt in den wesentlichen Abläufen ohne JavaScript nutzbar und
|
||
verwendet htmx für komfortable Teilaktualisierungen. Eine installierbare PWA stellt
|
||
eine Offline-App-Shell bereit; die eigentlichen Gartendaten bleiben serverseitig.
|
||
|
||
> **Projektstatus:** Gardomatic wird aktiv entwickelt. Datenmodell, API und
|
||
> Bedienoberfläche können sich noch verändern.
|
||
|
||
## Funktionen
|
||
|
||
- mehrere voneinander isolierte Gärten pro Benutzer
|
||
- gemeinsame Gartenpflege mit Einladungen, Rollen und Berechtigungen
|
||
- Verwaltung von Arten, Sorten, konkreten Pflanzen und Pflanzorten
|
||
- Pflegehinweise, Kategorien, Tags und Bilder
|
||
- Aufgaben mit Fälligkeitsfenstern, Prioritäten und Wiederholungen
|
||
- Aufgabenvorlagen pro Art und automatische, idempotente Aufgabenerzeugung
|
||
- Aufgabenansicht und Kalenderdarstellung
|
||
- Gartentagebuch, Pinnwand und Bildbibliothek
|
||
- Benutzerkonto, Aktivierung, Sitzungsverwaltung und Passwortänderung
|
||
- administrative Einladung, Anonymisierung und Verwaltung von Benutzern, Rollen und Anwendungseinstellungen inklusive maskierter Laufzeitkonfiguration und Testmailversand
|
||
- Kommandozeilenwerkzeug zur Benutzer- und Datenbankadministration
|
||
|
||
## Architektur
|
||
|
||
```text
|
||
Browser
|
||
│
|
||
▼
|
||
Webanwendung :4040 ──► typisierter API-Client
|
||
│
|
||
▼
|
||
JSON-API :4000
|
||
│
|
||
▼
|
||
Storage-Schnittstellen
|
||
│
|
||
▼
|
||
PostgreSQL 16
|
||
```
|
||
|
||
Die API ist die maßgebliche Sicherheits- und Fachgrenze. Sie verwaltet
|
||
Authentifizierung, Sitzungen, Autorisierung, Validierung und Datenzugriff. Die
|
||
Webanwendung greift nicht direkt auf die Datenbank zu, sondern leitet das
|
||
Session-Cookie eines Requests über `lib/client` an die API weiter.
|
||
|
||
Gartenbezogene Browserrouten beginnen mit `/g/{gardenID}/`, die entsprechenden
|
||
API-Ressourcen mit `/v1/gardens/{gardenID}/`. Ein Garten bildet eine abgeschlossene
|
||
Daten- und Berechtigungsgrenze.
|
||
|
||
### Programme
|
||
|
||
| Programm | Aufgabe | Standardadresse |
|
||
| --- | --- | --- |
|
||
| `cmd/api` | JSON-API und Fachlogik | `http://localhost:4000` |
|
||
| `cmd/web` | serverseitig gerenderte Weboberfläche | `http://localhost:4040` |
|
||
| `cmd/cli` | Administration direkt über PostgreSQL | keine |
|
||
|
||
## Voraussetzungen
|
||
|
||
Für den vollständigen lokalen Betrieb werden benötigt:
|
||
|
||
- Go gemäß der Version in `go.mod` (aktuell Go 1.26 oder neuer)
|
||
- PostgreSQL 16
|
||
- optional Docker oder Podman mit Compose-Unterstützung
|
||
- [`golang-migrate`](https://github.com/golang-migrate/migrate) für lokale
|
||
Migrationen und Integrationstests
|
||
- `make` für die bereitgestellten Entwicklungsbefehle
|
||
|
||
Die Compose-Variante bringt PostgreSQL und `golang-migrate` bereits als Container
|
||
mit. Go wird dort nur benötigt, wenn das Administrations-CLI auf dem Host verwendet
|
||
werden soll.
|
||
|
||
## Schnellstart mit Compose
|
||
|
||
1. Konfiguration anlegen und mindestens das Datenbankpasswort ändern:
|
||
|
||
```sh
|
||
cp .env.example .env
|
||
```
|
||
|
||
`POSTGRES_PASSWORD` und das Passwort in `GARDOMATIC_DB_DSN` sollten
|
||
übereinstimmen, damit auch lokale CLI- und Testbefehle dieselbe Datenbank
|
||
erreichen können. `.env` ist von Git ausgeschlossen.
|
||
|
||
2. Datenbank, Migrationen, API und Webanwendung starten:
|
||
|
||
```sh
|
||
docker compose up --build
|
||
```
|
||
|
||
Bei Podman kann je nach Installation stattdessen `podman compose` oder
|
||
`podman-compose` verwendet werden.
|
||
|
||
3. Einen ersten aktiven Benutzer anlegen. Dazu in einem zweiten Terminal die DSN
|
||
aus der lokalen `.env` setzen und das CLI starten:
|
||
|
||
```sh
|
||
export GARDOMATIC_DB_DSN='postgres://gardomatic:change-me@localhost:5432/gardomatic?sslmode=disable'
|
||
go run ./cmd/cli users create \
|
||
--name 'Admin' \
|
||
--email 'admin@example.com' \
|
||
--role application:admin \
|
||
--active \
|
||
--generate-password
|
||
```
|
||
|
||
Das generierte Passwort wird einmalig ausgegeben.
|
||
|
||
4. [http://localhost:4040](http://localhost:4040) öffnen und anmelden.
|
||
|
||
Die API-Gesundheitsprüfung ist unter
|
||
[http://localhost:4000/v1/healthcheck](http://localhost:4000/v1/healthcheck), die
|
||
Web-Gesundheitsprüfung unter [http://localhost:4040/ping](http://localhost:4040/ping)
|
||
erreichbar. Eine lesbare Statusseite mit API- und Serverinformationen steht unter
|
||
[http://localhost:4040/healtcheck](http://localhost:4040/healtcheck) bereit.
|
||
|
||
Den Stack beendet `docker compose down`. Die PostgreSQL-Daten liegen im benannten
|
||
Volume `gardomatic-postgres-data` und bleiben dabei erhalten. `docker compose down
|
||
-v` löscht dieses Volume und damit die lokale Datenbank dauerhaft.
|
||
|
||
## Lokale Entwicklung ohne Anwendungscontainer
|
||
|
||
PostgreSQL und die Migrationen können weiterhin über Compose laufen:
|
||
|
||
```sh
|
||
cp .env.example .env
|
||
docker compose up -d postgres migrate
|
||
```
|
||
|
||
Der `Makefile` bindet eine lokale, nicht versionierte `.envrc` ein. Mindestens die
|
||
Datenbankverbindung muss darin für API-, CLI- und Datenbankbefehle exportiert sein:
|
||
|
||
```sh
|
||
export GARDOMATIC_DB_DSN='postgres://gardomatic:change-me@localhost:5432/gardomatic?sslmode=disable'
|
||
```
|
||
|
||
Danach API und Webanwendung in getrennten Terminals starten:
|
||
|
||
```sh
|
||
make run/api
|
||
```
|
||
|
||
```sh
|
||
make run/web
|
||
```
|
||
|
||
Alternativ können beide Programme direkt mit `go run ./cmd/api` und
|
||
`go run ./cmd/web` gestartet werden, sofern die benötigten Umgebungsvariablen in
|
||
der Shell gesetzt sind. Die API benötigt zwingend `GARDOMATIC_DB_DSN`; alle
|
||
anderen Entwicklungswerte besitzen sinnvolle Standardwerte.
|
||
|
||
## Konfiguration
|
||
|
||
`.env.example` dokumentiert eine vollständige lokale Konfiguration. Geheimnisse
|
||
gehören ausschließlich in `.env`, `.envrc`, einen Secret Store oder die
|
||
Produktionsumgebung und dürfen nicht eingecheckt werden.
|
||
|
||
### Compose-Variablen
|
||
|
||
| Variable | Standard/Beispiel | Beschreibung |
|
||
| --- | --- | --- |
|
||
| `POSTGRES_DB` | `gardomatic` | Datenbankname des PostgreSQL-Containers |
|
||
| `POSTGRES_USER` | `gardomatic` | Datenbankbenutzer des Containers |
|
||
| `POSTGRES_PASSWORD` | `change-me` | Datenbankpasswort; lokal unbedingt ändern |
|
||
| `POSTGRES_PORT` | `5432` | auf dem Host veröffentlichter PostgreSQL-Port |
|
||
| `API_PORT` | `4000` | auf dem Host veröffentlichter API-Port |
|
||
| `WEB_PORT` | `4040` | auf dem Host veröffentlichter Web-Port |
|
||
|
||
### Laufzeitvariablen
|
||
|
||
| Variable | Standard | Verwendung |
|
||
| --- | --- | --- |
|
||
| `GARDOMATIC_ENV` | `development` | `development`, `test` oder `production` |
|
||
| `GARDOMATIC_DB_DSN` | erforderlich | PostgreSQL-Verbindungszeichenfolge für API und CLI |
|
||
| `GARDOMATIC_DB_MAX_OPEN_CONNS` | `25` | maximale offene DB-Verbindungen |
|
||
| `GARDOMATIC_DB_MAX_IDLE_CONNS` | `25` | maximale ungenutzte DB-Verbindungen |
|
||
| `GARDOMATIC_DB_MAX_IDLE_TIME` | `15m` | maximale Leerlaufzeit einer DB-Verbindung |
|
||
| `GARDOMATIC_API_HOST` | leer | Bind-Adresse der API |
|
||
| `GARDOMATIC_API_PORT` | `4000` | Listener-Port der API |
|
||
| `GARDOMATIC_WEB_HOST` | leer | Bind-Adresse der Webanwendung |
|
||
| `GARDOMATIC_WEB_PORT` | `4040` | Listener-Port der Webanwendung |
|
||
| `GARDOMATIC_API_BASE_URL` | `http://localhost:4000` | API-Adresse für den Web-Client |
|
||
| `GARDOMATIC_WEB_BASE_URL` | `http://localhost:4040` | öffentliche Basis-URL für Links aus API und CLI |
|
||
| `GARDOMATIC_SESSION_COOKIE_NAME` | `gardomatic_session` | gemeinsamer Name des Session-Cookies |
|
||
| `GARDOMATIC_SESSION_LIFETIME` | `12h` | absolute Lebensdauer einer Sitzung |
|
||
| `GARDOMATIC_SESSION_IDLE_TIMEOUT` | `30m` | Ablaufzeit bei Inaktivität |
|
||
| `GARDOMATIC_COOKIE_SECURE` | `false` | nur HTTPS-Cookies; in Produktion zwingend `true` |
|
||
| `GARDOMATIC_RATE_LIMIT_ENABLED` | `true` | API-Ratenbegrenzung aktivieren |
|
||
| `GARDOMATIC_RATE_LIMIT_RPS` | `10` | erlaubte Requests pro Sekunde |
|
||
| `GARDOMATIC_RATE_LIMIT_BURST` | `40` | kurzfristig erlaubte Request-Spitze |
|
||
| `GARDOMATIC_CORS_TRUSTED_ORIGINS` | leer/lokal gesetzt | kommaseparierte erlaubte Origins |
|
||
|
||
### E-Mail-Versand
|
||
|
||
| Variable | Standard | Beschreibung |
|
||
| --- | --- | --- |
|
||
| `GARDOMATIC_SMTP_MODE` | `file` | `file` für Entwicklung oder `smtp` |
|
||
| `GARDOMATIC_SMTP_HOST` | leer | SMTP-Server |
|
||
| `GARDOMATIC_SMTP_PORT` | `25` | SMTP-Port |
|
||
| `GARDOMATIC_SMTP_USERNAME` | leer | SMTP-Benutzername |
|
||
| `GARDOMATIC_SMTP_PASSWORD` | leer | SMTP-Passwort |
|
||
| `GARDOMATIC_SMTP_SENDER` | `gardomatic@localhost` | Absenderadresse |
|
||
| `GARDOMATIC_SMTP_FILE_PATH` | `/tmp/gardomatic-mails.log` | Ausgabe im `file`-Modus |
|
||
|
||
Im Entwicklungsmodus schreibt der voreingestellte `file`-Mailer E-Mails in die
|
||
angegebene Datei. In der Compose-API liegt diese Datei innerhalb des Containers;
|
||
sie kann beispielsweise mit `docker compose exec api cat
|
||
/tmp/gardomatic-mails.log` gelesen werden.
|
||
|
||
## Datenbankmigrationen
|
||
|
||
Migrationen liegen als fortlaufend nummerierte Up-/Down-Paare in
|
||
`internal/storage/postgres/migrations`.
|
||
|
||
Eine neue Migration anlegen:
|
||
|
||
```sh
|
||
make db/migrations/new name=describe_change
|
||
```
|
||
|
||
Alle ausstehenden Migrationen anwenden:
|
||
|
||
```sh
|
||
make db/migrations/up
|
||
```
|
||
|
||
Alle angewendeten Migrationen zurücknehmen:
|
||
|
||
```sh
|
||
make db/migrations/down
|
||
```
|
||
|
||
`migrate down` ohne Schrittzahl setzt das gesamte Schema zurück und kann sämtliche
|
||
Anwendungsdaten löschen. Die Make-Ziele verwenden `GARDOMATIC_DB_DSN`; vor Up- und
|
||
besonders Down-Befehlen daher immer prüfen, auf welche Datenbank die Variable zeigt.
|
||
Bereits veröffentlichte Migrationen sollten nicht verändert werden;
|
||
Schemaänderungen erhalten eine neue Migration.
|
||
|
||
## Tests und Qualitätsprüfungen
|
||
|
||
Alle Unit- und Handler-Tests ausführen:
|
||
|
||
```sh
|
||
go test ./...
|
||
```
|
||
|
||
Die PostgreSQL-Integrationstests werden ohne `GARDOMATIC_TEST_DB_DSN` automatisch
|
||
übersprungen. Für einen vollständigen Integrationslauf eine separate Testdatenbank
|
||
konfigurieren und zuerst migrieren:
|
||
|
||
```sh
|
||
export GARDOMATIC_TEST_DB_DSN='postgres://user:password@localhost/gardomatic_test?sslmode=disable'
|
||
migrate -path ./internal/storage/postgres/migrations -database "$GARDOMATIC_TEST_DB_DSN" up
|
||
go test -count=1 ./internal/storage/postgres
|
||
```
|
||
|
||
Alternativ migriert folgendes Ziel die in `GARDOMATIC_DB_DSN` konfigurierte
|
||
Datenbank und führt die Integrationstests dagegen aus:
|
||
|
||
```sh
|
||
make test/integration
|
||
```
|
||
|
||
Dieses Ziel ausschließlich mit einer entbehrlichen Testdatenbank verwenden.
|
||
|
||
Der vollständige lokale Qualitätslauf umfasst Modulprüfung, `go vet`,
|
||
`staticcheck` und Tests mit Race Detector:
|
||
|
||
```sh
|
||
make audit
|
||
```
|
||
|
||
Quelltext modernisieren und formatieren:
|
||
|
||
```sh
|
||
make tidy
|
||
```
|
||
|
||
`make tidy` kann `go.mod`, `go.sum`, den Vendor-Bestand und Go-Quelltext verändern.
|
||
Den resultierenden Diff deshalb immer prüfen.
|
||
|
||
Die CI unter `.github/workflows/ci.yml` ist dafür konfiguriert, PostgreSQL zu
|
||
migrieren, Module und Quelltext zu prüfen, alle Tests mit Race Detector auszuführen
|
||
und alle drei Programme zu bauen.
|
||
|
||
## Administration mit dem CLI
|
||
|
||
Das CLI benötigt für Datenbankbefehle `GARDOMATIC_DB_DSN`. Globale Optionen stehen
|
||
vor dem Unterbefehl. Mit `--json` liefert es maschinenlesbare Ausgabe; `--yes`
|
||
bestätigt bewusst konfigurierte Produktionsoperationen.
|
||
|
||
Häufige Befehle:
|
||
|
||
```sh
|
||
go run ./cmd/cli users list
|
||
go run ./cmd/cli users show --email alice@example.com
|
||
go run ./cmd/cli users invite --email alice@example.com --send-email
|
||
go run ./cmd/cli users activate --email alice@example.com
|
||
go run ./cmd/cli users deactivate --email alice@example.com
|
||
go run ./cmd/cli users reset-password --email alice@example.com --generate-password
|
||
go run ./cmd/cli db ping
|
||
```
|
||
|
||
Passwörter werden absichtlich nicht als Kommandozeilenargument angenommen. Sie
|
||
werden sicher abgefragt, generiert oder mit `--password-stdin` von der
|
||
Standardeingabe gelesen. Eine ausführliche Referenz mit Beispielen enthält
|
||
[`doc/cli.md`](doc/cli.md).
|
||
|
||
## Build
|
||
|
||
Einzelne Programme für das lokale System und Linux/AMD64 bauen:
|
||
|
||
```sh
|
||
make build/api
|
||
make build/web
|
||
make build/cli
|
||
```
|
||
|
||
Alle Programme bauen:
|
||
|
||
```sh
|
||
make build/all
|
||
```
|
||
|
||
Artefakte landen unter `bin/` und werden nicht versioniert. Der `Dockerfile`
|
||
enthält getrennte, minimale Laufzeit-Targets für API und Web.
|
||
|
||
## Projektstruktur
|
||
|
||
```text
|
||
cmd/
|
||
api/ Start und Konfiguration der JSON-API
|
||
web/ Start und Konfiguration der Webanwendung
|
||
cli/ Administrations-CLI
|
||
internal/
|
||
api/ API-Handler, Middleware und Fachabläufe
|
||
auth/ Passwort- und Tokenfunktionen
|
||
mailer/ Mailversand und Vorlagen
|
||
platform/ kleine technische Basispakete
|
||
storage/ Modelle und Storage-Schnittstellen
|
||
postgres/ PostgreSQL-Implementierungen und Migrationen
|
||
web/ Handler, Templates und statische Assets
|
||
lib/
|
||
client/ typisierter Go-Client für die JSON-API
|
||
doc/ Planung und weiterführende Dokumentation
|
||
remote/ Produktionsbeispiele für systemd und Caddy
|
||
request/ manuelle HTTP-Beispielanfragen
|
||
```
|
||
|
||
## Entwicklungskonventionen
|
||
|
||
Bei Änderungen sind insbesondere folgende Grundsätze verbindlich:
|
||
|
||
- idiomatischer, mit `gofmt` formatierter Go-Code
|
||
- klare Verantwortlichkeiten und bestehende Schichtengrenzen
|
||
- Wiederverwendung vorhandener Helfer und Fachlogik statt Codeduplizierung
|
||
- Tests für neues oder korrigiertes Verhalten, soweit technisch möglich
|
||
- neue Migrationen statt Änderungen an bereits veröffentlichten Migrationen
|
||
- keine Zugangsdaten oder lokalen Umgebungsdateien im Repository
|
||
|
||
Ausführliche Arbeitsregeln für Coding Agents und Beitragende stehen in
|
||
[`AGENTS.md`](AGENTS.md).
|
||
|
||
## Beiträge
|
||
|
||
Beiträge sind willkommen. Der vollständige Ablauf, Qualitätsanforderungen und der
|
||
Umgang mit Fremdmaterial sind in [`CONTRIBUTING.md`](CONTRIBUTING.md) beschrieben.
|
||
|
||
Vor dem ersten Pull Request müssen Beitragende die
|
||
[`Contributor License Agreement`](CLA.md) lesen und im Pull Request selbst
|
||
akzeptieren. Beitragende behalten ihr Copyright, räumen dem Projektinhaber jedoch
|
||
die notwendigen Rechte ein, den Beitrag sowohl unter der öffentlichen
|
||
Projektlizenz als auch unter separaten kommerziellen oder proprietären Lizenzen zu
|
||
verwenden. Beiträge im Namen eines Unternehmens müssen vorab abgestimmt werden.
|
||
|
||
## Deployment
|
||
|
||
Der `Dockerfile` kann Images für API und Web erzeugen. Unter `remote/production`
|
||
liegen außerdem Beispielkonfigurationen für systemd und Caddy. Die
|
||
`production/*`-Ziele im `Makefile` sind auf die vorhandene Gardomatic-Infrastruktur
|
||
zugeschnitten, enthalten einen fest konfigurierten Zielhost und führen Migrationen
|
||
sowie Dienstneustarts aus. Sie sind keine allgemeine Deployment-Anleitung und
|
||
sollten vor jeder Verwendung geprüft werden.
|
||
|
||
Die lokale Produktionsverbindung wird in `config.mk` konfiguriert. Eine kommentierte
|
||
Vorlage mit Zielhost, SSH-Admin, Port, optionalem privaten Schlüssel und
|
||
Zielarchitektur steht in `config.mk.example`. Der SSH-Admin ist der vom Hoster oder
|
||
bei der LXC-Erstellung bereitgestellte Benutzer (`root`, `ubuntu` oder ähnlich)
|
||
und benötigt Root-Rechte oder passwortloses `sudo`. Private SSH-Schlüssel bleiben
|
||
ausschließlich auf dem lokalen Rechner; auf dem Server muss vorab nur der
|
||
zugehörige öffentliche Schlüssel für diesen Admin hinterlegt sein.
|
||
|
||
Das Server-Setup unter `remote/setup/provision-server.sh` liest seine Konfiguration
|
||
aus `remote/setup/.env`. Als Ausgangspunkt dient `remote/setup/.env.example`; die
|
||
echte Datei muss auf Modus `0600` gesetzt werden und bleibt von Git ausgeschlossen.
|
||
`make production/provision` überträgt das Skript und streamt die Konfiguration über
|
||
SSH, ohne die Quelldatei dauerhaft auf dem Server abzulegen. Das Provisioning legt
|
||
den gesperrten Servicebenutzer `gardomatic` ohne Login, SSH-Schlüssel oder
|
||
sudo-Rechte an und installiert die Laufzeitwerte als
|
||
`/etc/gardomatic/gardomatic.env`.
|
||
|
||
Ein vollständiger Erstbetrieb besteht aus:
|
||
|
||
```sh
|
||
make config/init
|
||
# Die benötigten lokalen Konfigurationsdateien bearbeiten, dann:
|
||
make production/provision
|
||
make production/deploy
|
||
make production/create-admin
|
||
```
|
||
|
||
`production/deploy` überträgt Binärdateien, Migrationen, systemd-Units und die
|
||
Admin-Hilfe über denselben SSH-Admin, wendet Migrationen an und startet die Dienste.
|
||
`production/create-admin` fragt interaktiv nach Name und E-Mail und erstellt den
|
||
Benutzer aktiviert und mit der Rolle `application:admin`; das sichere generierte
|
||
Passwort wird einmalig ausgegeben.
|
||
|
||
Für Produktion gelten mindestens folgende Anforderungen:
|
||
|
||
- `GARDOMATIC_ENV=production`
|
||
- HTTPS am Reverse Proxy
|
||
- `GARDOMATIC_COOKIE_SECURE=true`
|
||
- starke, extern verwaltete Zugangsdaten
|
||
- eingeschränkter Datenbankzugriff und regelmäßige Backups
|
||
- korrekte öffentliche Web-URL und vertrauenswürdige CORS-Origins
|
||
- SMTP statt dateibasiertem Mailversand, sofern E-Mails zugestellt werden sollen
|
||
|
||
## Weiterführende Dokumentation
|
||
|
||
- [`doc/planung.md`](doc/planung.md) – Produktbeschreibung, Leitplanken und Fahrplan
|
||
- [`doc/cli.md`](doc/cli.md) – vollständige Bedienung des Administrations-CLI
|
||
- [`doc/issues.md`](doc/issues.md) – bekannte Themen und Arbeitsnotizen
|
||
- [`doc/ideen.md`](doc/ideen.md) – mögliche spätere Erweiterungen
|
||
|
||
## Lizenz
|
||
|
||
Gardomatic steht unter der
|
||
[PolyForm Noncommercial License 1.0.0](LICENSE). Sie erlaubt Nutzung, Veränderung
|
||
und Weitergabe für nichtkommerzielle Zwecke unter den Bedingungen der Lizenz.
|
||
|
||
Kommerzielle Nutzung ist von dieser Lizenz nicht abgedeckt und erfordert eine
|
||
separate, kostenpflichtige Lizenzvereinbarung. Anfragen können an
|
||
[alex@kleiax.de](mailto:alex@kleiax.de) gerichtet werden.
|
||
|
||
Für Beiträge Dritter gilt zusätzlich die
|
||
[`Gardomatic Contributor License Agreement`](CLA.md).
|
||
|
||
Wegen des Ausschlusses kommerzieller Nutzung ist Gardomatic „source-available“,
|
||
aber keine Open-Source-Software nach der Definition der Open Source Initiative.
|