docs: overhaul repository documentation
This commit is contained in:
@ -55,6 +55,8 @@ By: Codex (GPT-5)
|
||||
| 15.01.2026 | ⚙️ Code | ID: Makefile-Healthcheck auf Authorization: Bearer angepasst. By: Codex (GPT-5) |
|
||||
| 15.01.2026 | 🏗️ Planning | ID: US_000039 und TASK_000042 fuer Doku-Audit angelegt. By: Codex (GPT-5) |
|
||||
| 15.01.2026 | 📝 Req | ID: US_000039 Doku-Audit abgeschlossen (Header-Versionen konsolidiert, Update-Docs korrigiert). By: Codex (GPT-5) |
|
||||
| 15.01.2026 | 🏗️ Planning | ID: EPIC_000012 und US_000040 sowie TASK_000043-TASK_000044 fuer Doku-Overhaul angelegt. By: Codex (GPT-5) |
|
||||
| 15.01.2026 | 📝 Req | ID: US_000040 Doku-Overhaul umgesetzt (neue Struktur, README, Archivierung). By: Codex (GPT-5) |
|
||||
|
||||
---
|
||||
## Legende
|
||||
|
||||
156
README.md
156
README.md
@ -1,121 +1,61 @@
|
||||
ID: README_000001 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Safe Kiddo Daemon
|
||||
|
||||
Service to lock/unlock local user accounts on kids' laptops with countdown, notifications, optional sound, and shutdown. Provides a REST API plus a small web UI for remote control; retains the original `sk.sh` script as legacy/CLI fallback.
|
||||
## Kurzbeschreibung
|
||||
Safe Kiddo Daemon ist ein lokaler Systemdienst, der Benutzerkonten auf Linux-Systemen sperrt/entsperrt, Sitzungen beendet und optional einen Shutdown ausloest. Der Dienst bietet eine REST-API und eine Web-UI fuer die Fernsteuerung. Ein Legacy-CLI-Script (`sk.sh`) bleibt als Fallback erhalten.
|
||||
|
||||
## Features
|
||||
- Disable/enable accounts, terminate sessions, optionally trigger shutdown.
|
||||
- Desktop notifications and optional sound during countdown.
|
||||
- Login-protected API with minimal web UI (PAM auth for root users, bearer token for calls).
|
||||
- Systemd-managed service, virtualenv-based deploy, remote update script.
|
||||
- Dry-run mode to validate flows without touching accounts.
|
||||
## Zielgruppe
|
||||
- Einsteiger: moechten den Dienst schnell installieren und erste Aktionen ausfuehren.
|
||||
- Admins/Operatoren: konfigurieren Auth, OIDC und Update-Mechanik.
|
||||
- Entwickler: verstehen Architektur, Module und Erweiterungspunkte.
|
||||
|
||||
## Quick Start (Local/Target Device)
|
||||
## Hauptfunktionen
|
||||
- Sperren/Entsperren lokaler Nutzerkonten inkl. Session-Management und optionalem Shutdown.
|
||||
- Benachrichtigungen und optionaler Sound waehrend Countdown.
|
||||
- PAM-Login (immer aktiv) und optionaler OIDC-Login.
|
||||
- Systemd-Service mit Installations- und Update-Skripten.
|
||||
- Update-Client-Integration (Enrollment, Manifest, Apply, Rollback, Logs).
|
||||
- Dry-Run-Modus zum sicheren Testen.
|
||||
|
||||
## Badges
|
||||
Derzeit keine offiziellen Badges, da im Repository keine CI, Coverage, Release oder Docker-Pipeline definiert ist.
|
||||
|
||||
## Quickstart (5 Minuten)
|
||||
```bash
|
||||
git clone <repo> /opt/sk
|
||||
# 1) Repo holen
|
||||
sudo mkdir -p /opt/sk
|
||||
sudo git clone ssh://git@git.wlkns.org:2222/stephan/kiddo /opt/sk
|
||||
# Hinweis: verwende hier die Repo-URL deiner Instanz
|
||||
cd /opt/sk
|
||||
|
||||
# 2) Installieren (legt User, env und Systemd-Unit an)
|
||||
./scripts/install.sh
|
||||
|
||||
# 3) Status pruefen
|
||||
sudo systemctl status skd.service
|
||||
|
||||
# 4) Login testen (PAM)
|
||||
curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d '{"username":"root","password":"example-password"}' \
|
||||
http://localhost/login
|
||||
```
|
||||
Then open `http://localhost/` and log in via PAM (default) to start quickly.
|
||||
Danach die Web-UI unter `http://localhost/` oeffnen und anmelden.
|
||||
|
||||
## Configuration
|
||||
Set in `/etc/skd/env` (see `env.example`):
|
||||
- PAM-Login ist immer aktiv. OIDC wird zusaetzlich angeboten, wenn konfiguriert.
|
||||
- `SKD_AUTH_SECRET`: HMAC secret for bearer tokens/cookies (set a strong value).
|
||||
- `SKD_TOKEN_TTL_SECONDS`: token lifetime (default 900s).
|
||||
- `SKD_AUTH_ALLOWED_USERS`: optional comma list of accounts allowed to log in (used for PAM and as an allowlist for OIDC claims).
|
||||
- `SKD_AUTH_ALLOWED_GROUPS`: groups whose members may log in (PAM only, default `sudo`).
|
||||
- `SKD_AUTH_PAM_SERVICE`: PAM service name; Ubuntu/Debian uses `/etc/pam.d/skd` (created by `scripts/install.sh`), other distros may prefer `login` or `sshd`.
|
||||
- `SKD_OIDC_*`: `ISSUER`, `CLIENT_ID`, `CLIENT_SECRET`, `REDIRECT_URI`, `SCOPES` to point at your OIDC provider; set `SKD_SESSION_COOKIE_SECURE=true` for HTTPS.
|
||||
- OIDC dynamic registration helper: `scripts/register_oidc_client.sh` (requires `OIDC_INITIAL_ACCESS_TOKEN` and `SKD_OIDC_ISSUER`; uses `SKD_OIDC_REDIRECT_URI` for the redirect). Run once during setup if your provider issues initial access tokens for client creation.
|
||||
- `SKD_ALLOWED_USERS`: optional comma list to limit manageable accounts (must exist on the system).
|
||||
- `SKD_DEFAULT_COUNTDOWN`, `SKD_DEFAULT_SOUND`, `SKD_NOTIFY_TIMEOUT`: behavior defaults.
|
||||
- `SKD_DRY_RUN=true` to test without real account changes or shutdown.
|
||||
- `SKD_SOUND_PLAYER`/`SKD_SOUND_FILE`, `SKD_NOTIFY_SEND_PATH` if defaults differ.
|
||||
- Update client:
|
||||
- `SKD_UPDATE_SERVICE_URL` (default `https://update.wlkns.org`)
|
||||
- `SKD_UPDATE_PROJECT_ID` (default `safe-kiddo-control`)
|
||||
- `SKD_UPDATE_ENROLL_TOKEN` (one-time enrollment token for `/update/enroll`)
|
||||
- `SKD_UPDATE_TOKEN` (API token for update service; or load from `SKD_UPDATE_TOKEN_FILE`)
|
||||
- `SKD_UPDATE_TOKEN_FILE` (default `/var/lib/skd/update_token`)
|
||||
- `SKD_UPDATE_INTERVAL` (seconds; default 3600)
|
||||
- `SKD_UPDATE_STATUS_FILE` (default `/var/lib/skd/update_status.json`)
|
||||
- `SKD_UPDATE_LOG_FILE` (default `/var/lib/skd/update_logs.jsonl`)
|
||||
Notes:
|
||||
- `./scripts/install.sh` will create `/etc/skd/env` from `env.example` if missing (edit afterwards) and ensure the `skd` service user/group exist.
|
||||
## Dokumentation
|
||||
- Einstieg und erster Lauf: `docs/GETTING_STARTED.md`
|
||||
- Nutzung (API, Web-UI, CLI): `docs/USAGE.md`
|
||||
- Konfiguration (ENV-Variablen): `docs/CONFIGURATION.md`
|
||||
- Architektur und Module: `docs/ARCHITECTURE.md`
|
||||
- Entwicklung: `docs/DEVELOPMENT.md`
|
||||
- Deployment & Betrieb: `docs/DEPLOYMENT.md`
|
||||
- FAQ: `docs/FAQ.md`
|
||||
- Troubleshooting: `docs/TROUBLESHOOTING.md`
|
||||
|
||||
## OIDC Setup
|
||||
OIDC ist optional. PAM bleibt immer verfuegbar; `SKD_AUTH_MODE` ist optional.
|
||||
1. Issuer muss der externen URL des Providers entsprechen (TLS trust erforderlich).
|
||||
2. OIDC Client registrieren (DCR), z.B.:
|
||||
```bash
|
||||
export SKD_OIDC_ISSUER="https://auth.example.org"
|
||||
export SKD_OIDC_REDIRECT_URI="https://<device-host>/login/oidc/callback"
|
||||
export OIDC_INITIAL_ACCESS_TOKEN="<initial-access-token>"
|
||||
./scripts/register_oidc_client.sh
|
||||
```
|
||||
3. Danach in `/etc/skd/env` setzen:
|
||||
```
|
||||
SKD_AUTH_MODE=oidc
|
||||
SKD_OIDC_ISSUER=...
|
||||
SKD_OIDC_CLIENT_ID=...
|
||||
SKD_OIDC_CLIENT_SECRET=...
|
||||
SKD_OIDC_REDIRECT_URI=...
|
||||
SKD_OIDC_SCOPES=openid profile email
|
||||
SKD_SESSION_COOKIE_SECURE=true
|
||||
```
|
||||
Hinweise:
|
||||
- Redirect-URI muss exakt sein (keine Wildcards).
|
||||
- Bei Host/Port-Aenderung neu registrieren und neue Credentials setzen.
|
||||
- Allowlist fuer OIDC: `SKD_AUTH_ALLOWED_USERS` prueft `preferred_username`, `email` oder `sub`.
|
||||
|
||||
## Running
|
||||
- Service: managed by systemd; `./scripts/install.sh` writes the unit dynamically to `/etc/systemd/system/skd.service` with the current repo path and restarts it (runs as root for PAM).
|
||||
- Manual run: `./scripts/run.sh` (uses `.venv`, defaults to `0.0.0.0:80`).
|
||||
- Login (PAM): `curl -X POST -H "Content-Type: application/json" -d '{"username":"root","password":"..."}' http://localhost/login`
|
||||
- Login (OIDC): open `http://localhost/login/oidc/start` → provider → redirected back with session cookie set.
|
||||
- Health: `curl -H "Authorization: Bearer <token>" http://localhost/health`
|
||||
|
||||
## OIDC Validation & Fallbacks
|
||||
- Validierungsschritte: `docs/oidc-validation.md` (State, Token-Exchange, Claims, Cookie).
|
||||
- Falls Discovery/JWKS nicht verfuegbar: OIDC deaktivieren und PAM nutzen.
|
||||
- Falls DCR nicht verfuegbar: Client manuell im IdP anlegen und `SKD_OIDC_*` setzen.
|
||||
- Bei Self-Signed TLS: CA im System trusten oder in Dev PAM nutzen.
|
||||
|
||||
## API (Bearer token via `/login`)
|
||||
- `GET /users` → `[{user, logged_in, account_locked}]` (manageable system users; excludes root)
|
||||
- `POST /users/{name}/disable` with JSON `{countdown?, sound?, message?}`
|
||||
- `POST /users/{name}/enable`
|
||||
- `GET /health`
|
||||
- `GET /me` (returns current user + auth mode when a session/bearer token is present)
|
||||
|
||||
Example:
|
||||
```bash
|
||||
token=$(curl -s -X POST -H "Content-Type: application/json" -d '{"username":"root","password":"..."}' http://localhost/login | jq -r .token)
|
||||
curl -X POST -H "Authorization: Bearer $token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"countdown":90,"sound":true}' \
|
||||
http://localhost/users/child1/disable
|
||||
```
|
||||
|
||||
## Web UI
|
||||
Served at `/`. Nutze den Button „Login via OIDC“ (setzt Session-Cookie) oder das PAM-Formular, falls OIDC deaktiviert; danach werden verfügbare System-User angezeigt und Aktionen senden Token/Cookies automatisch.
|
||||
|
||||
## Updates
|
||||
- Remote update via SSH: `ssh user@kid-laptop 'cd /opt/sk && ./scripts/update.sh'` (fetch/reset to `origin/main`, reinstalls deps, restarts service).
|
||||
- Manual: `git pull && source .venv/bin/activate && pip install -r backend/requirements.txt && sudo systemctl restart skd`
|
||||
|
||||
## Deployment (zip/SSH)
|
||||
- Quick copy: create `sk_deploy.zip` (already in repo root) and unzip on target under `/opt/sk`, then refresh venv deps and restart service.
|
||||
- Scripted deploy: edit `deploy_hosts.yml` (host/user/port/install_dir/service user/group), then run `./scripts/deploy.sh <host-name>`; accepts JSON configs too. Requires SSH access and `sudo` on target.
|
||||
- After deploy on target: `sudo -u skd /opt/sk/.venv/bin/pip install -r /opt/sk/backend/requirements.txt && sudo systemctl restart skd.service`
|
||||
|
||||
## Security Hardening
|
||||
- Restrict access to API/Web UI to LAN/VPN; firewall the port.
|
||||
- Set a strong `SKD_AUTH_SECRET`; rotate tokens by changing the secret.
|
||||
- Create dedicated `skd` user/group; no login shell.
|
||||
- Configure sudoers minimally: allow `skd` to run `usermod -L/-U`, `pkill -KILL -u`, `shutdown now`, and sound/notify binaries if needed (no full passwordless sudo).
|
||||
- Consider mTLS or IP allowlisting for added protection.
|
||||
## Hinweise
|
||||
- Abweichung von der Zielstruktur: Die OpenAPI-Spezifikation des Update-Services bleibt unter `docs/architecture/openapi.yaml` und `docs/architecture/openapi/` erhalten.
|
||||
- Altdokumente liegen unter `docs/_archive/` und sind nicht geloescht.
|
||||
|
||||
## Legacy Script
|
||||
`sk.sh` remains for direct SSH use. Plan to replace its logic with API-backed helpers; keep it as emergency fallback.
|
||||
Das Script `sk.sh` bleibt als CLI-Fallback. Details und Beispiele: `docs/USAGE.md`.
|
||||
|
||||
59
docs/ARCHITECTURE.md
Normal file
59
docs/ARCHITECTURE.md
Normal file
@ -0,0 +1,59 @@
|
||||
ID: DOC_000012 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Architektur
|
||||
|
||||
## Systemuebersicht
|
||||
Safe Kiddo Daemon ist ein FastAPI-basierter Service, der lokale Systemkonten verwaltet. Er kombiniert:
|
||||
- API und Web-UI (FastAPI + Jinja2 Templates)
|
||||
- Authentifizierung (PAM und optional OIDC)
|
||||
- Systemaktionen via sudo/usermod/pkill/shutdown
|
||||
- Update-Client-Integration (externes Update-Service-Backend)
|
||||
|
||||
## Module und Verantwortlichkeiten
|
||||
- `backend/app.py`: API-Routing, Web-UI-Endpunkte, Update-Endpunkte.
|
||||
- `backend/auth.py`: PAM-Login, JWT-Handling, Auth-Guards, Allowlists.
|
||||
- `backend/oidc.py`: OIDC Discovery, Token-Exchange, JWT-Validierung.
|
||||
- `backend/actions.py`: Systemaktionen (lock/unlock, notify, sound, shutdown).
|
||||
- `backend/update.py`: Update-Enrollment, Manifest-Check, Update/Rollback-Start, Status/Logs.
|
||||
- `backend/settings.py`: Zentrale ENV-Konfiguration.
|
||||
- `backend/templates/` + `backend/static/`: Web-UI.
|
||||
- `scripts/*.sh`: Installation, Deployment, Update-Client, Rollback, OIDC-Registration.
|
||||
|
||||
## Daten- und Kontrollfluss
|
||||
### Login und Auth
|
||||
1. `POST /login` authentifiziert via PAM.
|
||||
2. JWT wird erstellt und als Cookie oder Bearer-Token genutzt.
|
||||
3. Schutz aller Admin-Endpunkte via `get_current_admin`.
|
||||
|
||||
### OIDC-Flow (optional)
|
||||
1. `GET /login/oidc/start` generiert State und leitet zum IdP.
|
||||
2. Callback `GET /login/oidc/callback` validiert State, tauscht Code gegen ID-Token.
|
||||
3. ID-Token wird gegen JWKS geprueft, Username extrahiert, Session gesetzt.
|
||||
|
||||
### Benutzeraktionen
|
||||
1. `POST /users/{username}/disable` ruft `actions.disable_user`.
|
||||
2. Systemaktionen: `usermod -L`, optional notify/sound, `pkill`, optional `shutdown`.
|
||||
3. `POST /users/{username}/enable` fuehrt `usermod -U` aus.
|
||||
|
||||
### Update-Flow
|
||||
1. `POST /update/enroll` schreibt Langzeit-Token in `SKD_UPDATE_TOKEN_FILE`.
|
||||
2. `POST /update/check` ruft Manifest beim Update-Service ab.
|
||||
3. `POST /update/apply` startet `scripts/update_client.sh` asynchron.
|
||||
4. `POST /update/rollback` startet `scripts/rollback_client.sh` asynchron.
|
||||
5. Status/Logs werden lokal in Dateien geschrieben und optional an den Update-Service gemeldet.
|
||||
|
||||
## Designentscheidungen und Tradeoffs
|
||||
- **Root-Run**: Service laeuft als root, da PAM und Systemkommandos Root erfordern.
|
||||
- **JWT + Cookie**: Einfache lokale Auth; keine externe Session-Datenbank.
|
||||
- **OIDC optional**: OIDC ist optional, PAM bleibt als Fallback aktiv.
|
||||
- **Update als Script**: Update/Backup/Swap via Bash-Skripte fuer einfache Ops, Tradeoff: weniger granularer Fehler-Handling.
|
||||
|
||||
## Erweiterungspunkte
|
||||
- **Auth**: Weitere Auth-Mechanismen koennen in `backend/auth.py` integriert werden.
|
||||
- **UI**: Templates unter `backend/templates/` und CSS in `backend/static/`.
|
||||
- **Update-Client**: Anpassung der Update-Strategie in `scripts/update_client.sh`.
|
||||
- **Notifications/Sound**: Konfigurierbar per `SKD_NOTIFY_SEND_PATH`, `SKD_SOUND_PLAYER`, `SKD_SOUND_FILE`.
|
||||
|
||||
## Spezifikationen
|
||||
- Update-Service OpenAPI: `docs/architecture/openapi.yaml` und `docs/architecture/openapi/`.
|
||||
54
docs/CONFIGURATION.md
Normal file
54
docs/CONFIGURATION.md
Normal file
@ -0,0 +1,54 @@
|
||||
ID: DOC_000011 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Konfiguration
|
||||
|
||||
## Speicherort
|
||||
Die Konfiguration erfolgt per ENV-Datei, standardmaessig `/etc/skd/env`. Vorlage: `env.example`.
|
||||
|
||||
## Authentifizierung
|
||||
- `SKD_AUTH_MODE` (default `pam`): `pam` oder `oidc`. Ungueltige Werte fallen auf `pam` zurueck. Hinweis: Der Wert wird aktuell nicht zur Erzwingung genutzt; OIDC ist aktiv, sobald die OIDC-Variablen gesetzt sind.
|
||||
- `SKD_AUTH_SECRET` (default `change-me-secret`): HMAC-Secret fuer JWTs.
|
||||
- `SKD_TOKEN_TTL_SECONDS` (default `900`): Token-Laufzeit in Sekunden.
|
||||
- `SKD_AUTH_ALLOWED_USERS` (default leer): Kommagetrennte Liste erlaubter Admin-User (gilt fuer PAM und OIDC).
|
||||
- `SKD_AUTH_ALLOWED_GROUPS` (default `sudo`): Erlaubte Gruppen fuer PAM-Login.
|
||||
- `SKD_AUTH_PAM_SERVICE` (default `login`, auf Debian/Ubuntu via `install.sh` auf `skd` gesetzt).
|
||||
|
||||
## OIDC
|
||||
OIDC ist optional und zusaetzlich zu PAM.
|
||||
- `SKD_OIDC_ISSUER`
|
||||
- `SKD_OIDC_CLIENT_ID`
|
||||
- `SKD_OIDC_CLIENT_SECRET`
|
||||
- `SKD_OIDC_REDIRECT_URI` (default `http://localhost:8000/login/oidc/callback`)
|
||||
- `SKD_OIDC_SCOPES` (default `openid profile email`)
|
||||
- `SKD_SESSION_COOKIE_SECURE` (default `false`): Setze `true` fuer HTTPS.
|
||||
- `SKD_OIDC_STATE_COOKIE_NAME` (default `skd_oidc_state`)
|
||||
|
||||
## Session/Benutzerverwaltung
|
||||
- `SKD_SESSION_COOKIE_NAME` (default `skd_session`)
|
||||
- `SKD_ALLOWED_USERS` (default leer): Optionales Allowlist fuer verwaltbare System-User.
|
||||
|
||||
## Aktionen (Countdown/Notify/Sound)
|
||||
- `SKD_DEFAULT_COUNTDOWN` (default `60` Sekunden)
|
||||
- `SKD_DEFAULT_SOUND` (default `false`)
|
||||
- `SKD_NOTIFY_TIMEOUT` (default `5` Sekunden)
|
||||
- `SKD_NOTIFY_SEND_PATH` (default `notify-send`)
|
||||
- `SKD_SOUND_PLAYER` (default `paplay`)
|
||||
- `SKD_SOUND_FILE` (default `/usr/share/sounds/freedesktop/stereo/dialog-warning.oga`)
|
||||
|
||||
## Update-Client
|
||||
- `SKD_UPDATE_SERVICE_URL` (default `https://update.wlkns.org`)
|
||||
- `SKD_UPDATE_PROJECT_ID` (default `safe-kiddo-control`)
|
||||
- `SKD_UPDATE_ENROLL_TOKEN` (optional; fuer `/update/enroll`)
|
||||
- `SKD_UPDATE_TOKEN` (optional; alternativ per Datei)
|
||||
- `SKD_UPDATE_TOKEN_FILE` (default `/var/lib/skd/update_token`)
|
||||
- `SKD_UPDATE_STATUS_FILE` (default `/var/lib/skd/update_status.json`)
|
||||
- `SKD_UPDATE_LOG_FILE` (default `/var/lib/skd/update_logs.jsonl`)
|
||||
- `SKD_UPDATE_INTERVAL` (default `3600`): Hinweis: wird aktuell nur eingelesen, aber nicht automatisch genutzt.
|
||||
|
||||
## Dry-Run
|
||||
- `SKD_DRY_RUN` (default `false`): Keine echten System-Aktionen, nur Logging.
|
||||
|
||||
## Hinweise
|
||||
- `scripts/install.sh` erstellt `/etc/skd/env` und setzt Default-Werte fuer PAM/Allowed-User.
|
||||
- Aenderungen in `/etc/skd/env` erfordern einen Service-Restart (`sudo systemctl restart skd.service`).
|
||||
68
docs/DEPLOYMENT.md
Normal file
68
docs/DEPLOYMENT.md
Normal file
@ -0,0 +1,68 @@
|
||||
ID: DOC_000014 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Deployment
|
||||
|
||||
## Lokale Installation (systemd)
|
||||
`./scripts/install.sh` fuehrt folgende Schritte aus:
|
||||
- legt Service-User/Group an
|
||||
- kopiert das Projekt nach `/opt/sk`
|
||||
- erstellt/aktualisiert `.venv`
|
||||
- erstellt `/etc/skd/env` aus `env.example`
|
||||
- schreibt eine systemd-Unit nach `/etc/systemd/system/skd.service`
|
||||
|
||||
Beispiel:
|
||||
```bash
|
||||
sudo ./scripts/install.sh
|
||||
sudo systemctl status skd.service
|
||||
```
|
||||
|
||||
## Manuelles Starten
|
||||
```bash
|
||||
./scripts/run.sh
|
||||
```
|
||||
|
||||
## Remote-Deploy (SSH)
|
||||
`./scripts/deploy.sh` packt das Repo und deployt es auf einen Zielhost.
|
||||
Konfiguration in `deploy_hosts.yml`.
|
||||
|
||||
Beispiel:
|
||||
```bash
|
||||
./scripts/deploy.sh kid-laptop
|
||||
```
|
||||
|
||||
## Deployment via ZIP
|
||||
Im Repo liegt `sk_deploy.zip`. Dieses Archiv kann auf den Zielhost kopiert und nach `/opt/sk` entpackt werden.
|
||||
Anschliessend Abhaengigkeiten installieren und Service neu starten:
|
||||
```bash
|
||||
sudo -u skd /opt/sk/.venv/bin/pip install -r /opt/sk/backend/requirements.txt
|
||||
sudo systemctl restart skd.service
|
||||
```
|
||||
## Update des Services
|
||||
`./scripts/update.sh` zieht den Branch neu und fuehrt einen harten Reset aus.
|
||||
|
||||
Wichtig: Das Script nutzt `git reset --hard origin/main`.
|
||||
```bash
|
||||
sudo ./scripts/update.sh
|
||||
```
|
||||
|
||||
## Update-Client (Remote Update Service)
|
||||
Die Update-API startet `scripts/update_client.sh` bzw. `scripts/rollback_client.sh`.
|
||||
Wichtige ENV-Variablen:
|
||||
- `SKD_UPDATE_SERVICE_URL`
|
||||
- `SKD_UPDATE_PROJECT_ID`
|
||||
- `SKD_UPDATE_TOKEN` oder `SKD_UPDATE_TOKEN_FILE`
|
||||
Voraussetzungen auf dem Host:
|
||||
- `curl`, `tar`, `sha256sum`, `python3`, `systemctl`
|
||||
|
||||
Enrollment-Tools:
|
||||
- `scripts/manual_enroll.py`: Enrollment direkt gegen den Update-Service, schreibt Token in `SKD_UPDATE_TOKEN_FILE`.
|
||||
- `scripts/enroll_local.py`: Enrollment ueber die lokale API (`/update/enroll`), benoetigt Admin-Session; `--token` setzen (Default-Token ist nur Prototyp-Altlast).
|
||||
|
||||
Lokale Status/Logs:
|
||||
- `SKD_UPDATE_STATUS_FILE` (default `/var/lib/skd/update_status.json`)
|
||||
- `SKD_UPDATE_LOG_FILE` (default `/var/lib/skd/update_logs.jsonl`)
|
||||
|
||||
## Backup/Restore
|
||||
- Bei Apply wird `/opt/sk` nach `/opt/sk_backup_1.2.3_1700000000` verschoben (Beispiel).
|
||||
- Rollback nutzt das letzte Backup (`/opt/sk_backup_*`).
|
||||
44
docs/DEVELOPMENT.md
Normal file
44
docs/DEVELOPMENT.md
Normal file
@ -0,0 +1,44 @@
|
||||
ID: DOC_000013 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Development
|
||||
|
||||
## Repository-Struktur (Kurz)
|
||||
- `backend/`: FastAPI-App, Auth, Update-Logik, Templates, Static Assets.
|
||||
- `scripts/`: Install/Deploy/Update/Helper-Skripte.
|
||||
- `systemd/`: Beispiel-Unit.
|
||||
- `docs/architecture/`: OpenAPI-Spezifikation fuer Update-Service.
|
||||
- `docs/`: Dokumentation.
|
||||
- `assets/`: Branding und Design.
|
||||
- `sk.sh`: Legacy-CLI.
|
||||
|
||||
## Lokales Setup
|
||||
```bash
|
||||
./scripts/create_venv.sh
|
||||
source .venv/bin/activate
|
||||
./scripts/run.sh
|
||||
```
|
||||
Standard: `0.0.0.0:80`. Fuer andere Ports:
|
||||
```bash
|
||||
HOST=127.0.0.1 PORT=8000 ./scripts/run.sh
|
||||
```
|
||||
|
||||
## Tests und Lint
|
||||
Im Repo sind keine automatisierten Tests enthalten. Verfuegbare Checks:
|
||||
- Bash-Syntax: `bash -n sk.sh`
|
||||
- ShellCheck: `shellcheck sk.sh`
|
||||
|
||||
## Coding Conventions
|
||||
- Bash 4+, `set -euo pipefail` in neuen Skripten.
|
||||
- Python: FastAPI-Patterns, klare Modultrennung (Auth, Actions, Update, OIDC).
|
||||
|
||||
## Beitrag und Workflow
|
||||
- Arbeite mit Feature-Branches.
|
||||
- Aktualisiere `VERSION`, Doku-Header und `CHANGELOG.md` gemaess SOP.
|
||||
- PRs sollten Verhalten, Risiken und manuelle Tests beschreiben.
|
||||
|
||||
## CI/CD
|
||||
Aktuell keine CI/CD-Pipeline im Repository definiert.
|
||||
|
||||
## Legacy/Interna
|
||||
- `SKD_UPDATE_URL` und `SKD_UPDATE_STATUS_URL` sind in `backend/settings.py` noch vorhanden, werden aber im aktuellen Code nicht genutzt.
|
||||
21
docs/FAQ.md
Normal file
21
docs/FAQ.md
Normal file
@ -0,0 +1,21 @@
|
||||
ID: DOC_000015 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# FAQ
|
||||
|
||||
## Braucht der Dienst Root-Rechte?
|
||||
Ja. PAM-Authentifizierung und Systemkommandos (usermod/pkill/shutdown) erfordern Root.
|
||||
|
||||
## Kann ich OIDC ohne PAM nutzen?
|
||||
OIDC ist optional und zusaetzlich zu PAM. PAM bleibt als Fallback aktiv.
|
||||
|
||||
## Wie aendere ich den Port?
|
||||
- Lokaler Run: `PORT=8000 ./scripts/run.sh`
|
||||
- Systemd: Unit-Datei in `/etc/systemd/system/skd.service` anpassen und Service neu starten.
|
||||
|
||||
## Gibt es einen Docker-Container?
|
||||
Nein, im Repository ist kein Docker-Setup enthalten.
|
||||
|
||||
## Wo liegen Logs?
|
||||
- systemd: `journalctl -u skd.service`
|
||||
- Update-Status/Logs: siehe `SKD_UPDATE_STATUS_FILE` und `SKD_UPDATE_LOG_FILE`.
|
||||
48
docs/GETTING_STARTED.md
Normal file
48
docs/GETTING_STARTED.md
Normal file
@ -0,0 +1,48 @@
|
||||
ID: DOC_000009 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Getting Started
|
||||
|
||||
## Ziel
|
||||
Schneller Einstieg fuer neue Nutzer: Installation, erster Login und erste Aktion.
|
||||
|
||||
## Voraussetzungen
|
||||
- Linux-System mit systemd.
|
||||
- Root-Zugriff (PAM, usermod, shutdown).
|
||||
- Python 3, curl, tar, sha256sum (fuer Update-Client-Skripte).
|
||||
- Optional: notify-send (Benachrichtigungen), paplay/aplay (Sound).
|
||||
|
||||
## Schnellstart
|
||||
```bash
|
||||
# 1) Repo installieren
|
||||
sudo mkdir -p /opt/sk
|
||||
sudo git clone ssh://git@git.wlkns.org:2222/stephan/kiddo /opt/sk
|
||||
# Hinweis: verwende hier die Repo-URL deiner Instanz
|
||||
cd /opt/sk
|
||||
|
||||
# 2) Installation (legt Service-User, env, systemd-Unit an)
|
||||
./scripts/install.sh
|
||||
|
||||
# 3) Service pruefen
|
||||
sudo systemctl status skd.service
|
||||
|
||||
# 4) Login (PAM)
|
||||
curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d '{"username":"root","password":"..."}' \
|
||||
http://localhost/login
|
||||
```
|
||||
|
||||
## Erster API-Test
|
||||
```bash
|
||||
token=$(curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d '{"username":"root","password":"example-password"}' \
|
||||
http://localhost/login | jq -r .token)
|
||||
|
||||
curl -s -H "Authorization: Bearer $token" http://localhost/me
|
||||
```
|
||||
Hinweis: `jq` ist optional; ohne jq das Token manuell aus der JSON-Antwort lesen.
|
||||
|
||||
## Naechste Schritte
|
||||
- Konfiguration anpassen: `docs/CONFIGURATION.md`
|
||||
- API und Web-UI nutzen: `docs/USAGE.md`
|
||||
- Deployment und Updates: `docs/DEPLOYMENT.md`
|
||||
37
docs/TROUBLESHOOTING.md
Normal file
37
docs/TROUBLESHOOTING.md
Normal file
@ -0,0 +1,37 @@
|
||||
ID: DOC_000016 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Service startet nicht
|
||||
- Status pruefen: `sudo systemctl status skd.service`
|
||||
- Logs: `sudo journalctl -u skd.service -n 200 --no-pager`
|
||||
- Port 80 belegt? Test: `sudo ss -ltnp | grep ':80'`
|
||||
|
||||
## 401/403 bei API-Aufrufen
|
||||
- Bearer-Token fehlt oder abgelaufen.
|
||||
- Nutzer nicht in `SKD_AUTH_ALLOWED_USERS` oder `SKD_AUTH_ALLOWED_GROUPS`.
|
||||
- `SKD_AUTH_SECRET` geaendert? Tokens muessen neu erzeugt werden.
|
||||
|
||||
## OIDC-Login fehlschlaegt
|
||||
- `SKD_OIDC_ISSUER`, `SKD_OIDC_CLIENT_ID`, `SKD_OIDC_CLIENT_SECRET` gesetzt?
|
||||
- Redirect-URI exakt registriert?
|
||||
- Netzwerkzugriff auf Discovery/JWKS moeglich?
|
||||
|
||||
## Benachrichtigung/Sound fehlt
|
||||
- `notify-send` fehlt: `sudo apt install libnotify-bin`
|
||||
- Sound-Player fehlt: `paplay` oder `aplay` installieren.
|
||||
- `SKD_NOTIFY_SEND_PATH` oder `SKD_SOUND_PLAYER` falsch gesetzt.
|
||||
|
||||
## Update-Check meldet "Client is not enrolled"
|
||||
- `SKD_UPDATE_TOKEN` oder `SKD_UPDATE_TOKEN_FILE` fehlt.
|
||||
- Enrollment ueber `/update/enroll` oder `scripts/manual_enroll.py` durchfuehren.
|
||||
|
||||
## Update-Apply fehlschlaegt
|
||||
- Update-Service nicht erreichbar oder Token ungueltig.
|
||||
- Prüfe `SKD_UPDATE_STATUS_FILE` und `SKD_UPDATE_LOG_FILE`.
|
||||
- Hinweis: `update_client.sh` wird asynchron gestartet und stdout/stderr werden verworfen.
|
||||
|
||||
## Rollback meldet "no backup found"
|
||||
- Es existiert kein `/opt/sk_backup_*` vom vorherigen Update.
|
||||
- Rollback erst nach mindestens einem erfolgreichen Update moeglich.
|
||||
80
docs/USAGE.md
Normal file
80
docs/USAGE.md
Normal file
@ -0,0 +1,80 @@
|
||||
ID: DOC_000010 | Version: 0.2.1 | Status: Draft
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Usage
|
||||
|
||||
## Authentifizierung
|
||||
- PAM-Login: `POST /login` mit Benutzername/Passwort. Liefert JWT und setzt Session-Cookie.
|
||||
- OIDC-Login: `GET /login/oidc/start` startet Flow, Callback setzt Session-Cookie.
|
||||
- Alle geschuetzten Endpunkte akzeptieren `Authorization: Bearer $TOKEN` oder Session-Cookie.
|
||||
|
||||
### Beispiel: Login und Token nutzen
|
||||
```bash
|
||||
token=$(curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d '{"username":"root","password":"example-password"}' \
|
||||
http://localhost/login | jq -r .token)
|
||||
|
||||
curl -s -H "Authorization: Bearer $token" http://localhost/me
|
||||
```
|
||||
Hinweis: `jq` ist optional; ohne jq das Token manuell aus der JSON-Antwort lesen.
|
||||
Login ist nur fuer erlaubte Nutzer moeglich (siehe `SKD_AUTH_ALLOWED_USERS` und `SKD_AUTH_ALLOWED_GROUPS`).
|
||||
|
||||
## Web-UI
|
||||
- Aufruf: `http://localhost/`
|
||||
- Login per PAM oder OIDC (wenn konfiguriert).
|
||||
- Aktionen: Benutzer sperren/entsperren, Update-Status, Update-Check, Apply/Rollback.
|
||||
|
||||
## API-Endpunkte (Auszug)
|
||||
### Health und Identitaet
|
||||
- `GET /health` (ohne Auth)
|
||||
- `GET /me`
|
||||
|
||||
### Benutzerverwaltung
|
||||
- `GET /users`
|
||||
- `POST /users/{username}/disable` mit JSON `{countdown?, sound?, message?}`
|
||||
- `POST /users/{username}/enable`
|
||||
|
||||
Beispiel (disable):
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer $token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"countdown":90,"sound":true,"message":"Bitte speichern"}' \
|
||||
http://localhost/users/child1/disable
|
||||
```
|
||||
|
||||
### Update-API (lokal)
|
||||
Alle Update-Endpunkte erfordern Admin-Auth.
|
||||
- `GET /update/status`
|
||||
- `POST /update/enroll` (optional Body: `{ "enroll_token": "..." }`)
|
||||
- `POST /update/check`
|
||||
- `POST /update/apply` (optional Body: `{ "version": "x.y.z" }`)
|
||||
- `POST /update/rollback`
|
||||
- `GET /update/logs?limit=200`
|
||||
|
||||
Beispiel (Enrollment):
|
||||
```bash
|
||||
ENROLL_TOKEN="example-enroll-token"
|
||||
curl -X POST -H "Authorization: Bearer $token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"enroll_token\":\"${ENROLL_TOKEN}\"}" \
|
||||
http://localhost/update/enroll
|
||||
```
|
||||
|
||||
Beispiel (Update-Check):
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer $token" http://localhost/update/check
|
||||
```
|
||||
Hinweis: Ohne gespeichertes Update-Token liefert der Check einen Fehler.
|
||||
|
||||
## CLI-Fallback (sk.sh)
|
||||
Das Legacy-Script arbeitet direkt auf dem Host und benoetigt Root-Rechte.
|
||||
|
||||
Aufruf:
|
||||
```bash
|
||||
sudo ./sk.sh USERNAME disable|enable [countdown] [sound] [countdown_time_in_seconds]
|
||||
```
|
||||
|
||||
Beispiel:
|
||||
```bash
|
||||
sudo ./sk.sh demo_user disable countdown sound 90
|
||||
```
|
||||
@ -1,4 +1,5 @@
|
||||
ID: DOC_000006 | Version: 0.2.1 | Status: Draft
|
||||
Archived – superseded by new documentation.
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Admin Token Operations
|
||||
@ -1,4 +1,5 @@
|
||||
ID: DOC_000008 | Version: 0.2.1 | Status: Draft
|
||||
Archived – superseded by new documentation.
|
||||
|
||||
# Client Quickstart
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
ID: DOC_000003 | Version: 0.2.1 | Status: Draft
|
||||
Archived – superseded by new documentation.
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# OIDC End-to-End Validation (Kiddo)
|
||||
@ -1,4 +1,5 @@
|
||||
ID: DOC_000005 | Version: 0.2.1 | Status: Draft
|
||||
Archived – superseded by new documentation.
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Third-Party API Guide
|
||||
@ -1,4 +1,5 @@
|
||||
ID: DOC_000006 | Version: 0.2.1 | Status: Draft
|
||||
Archived – superseded by new documentation.
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Update API (Kiddo Backend)
|
||||
@ -1,4 +1,5 @@
|
||||
ID: DOC_000004 | Version: 0.2.1 | Status: Draft
|
||||
Archived – superseded by new documentation.
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Client Update Flow (Kiddo)
|
||||
@ -1,4 +1,5 @@
|
||||
ID: DOC_000005 | Version: 0.2.1 | Status: Draft
|
||||
Archived – superseded by new documentation.
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# Update Status Reporting
|
||||
@ -19,6 +19,7 @@ Sicheres, remote steuerbares System zum Sperren/Entsperren lokaler Nutzerkonten.
|
||||
|---|---|---|
|
||||
| 28.12.2025 | 🏗️ Planning | Anforderungen als Epics und Stories dokumentiert. |
|
||||
| 15.01.2026 | 📝 Req | Doku und ENV-Beispiele an Code-Stand angepasst. |
|
||||
| 15.01.2026 | 📝 Req | Doku-Overhaul mit neuer Struktur und Archivierung. |
|
||||
|
||||
## Epic-Backlog (Uebersicht)
|
||||
### EPIC_000001: Legacy CLI Account Control (sk.sh)
|
||||
@ -122,6 +123,11 @@ Sicheres, remote steuerbares System zum Sperren/Entsperren lokaler Nutzerkonten.
|
||||
- [x] US_000039: Doku-Audit fuer verbleibende Abweichungen
|
||||
- [x] TASK_000042: Doku-Audit verbleibender Dateien
|
||||
|
||||
### EPIC_000012: Dokumentations-Overhaul
|
||||
- [x] US_000040: Doku-Struktur und Inhalte erstellen
|
||||
- [x] TASK_000043: Neue Doku-Dateien erstellen und verlinken
|
||||
- [x] TASK_000044: Altdoku archivieren
|
||||
|
||||
## Offene Risiken / Abhaengigkeiten
|
||||
- Betrieb erfordert Root/sudo und lokale System-Tools (notify-send, sound player, uvicorn).
|
||||
- OIDC-Validierung blockiert bis IdP bereit und Service laeuft.
|
||||
|
||||
42
project-management/requirements/epics/EPIC_000012.md
Normal file
42
project-management/requirements/epics/EPIC_000012.md
Normal file
@ -0,0 +1,42 @@
|
||||
ID: EPIC_000012 | Version: 0.2.1 | Status: Done
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# EPIC_000012: Dokumentations-Overhaul
|
||||
|
||||
## Beschreibung
|
||||
Komplette, konsistente Dokumentation fuer Einsteiger, Admins und Entwickler, inkl. Archivierung
|
||||
veralteter Inhalte und einer klaren Doku-Struktur.
|
||||
|
||||
## Ziel / Business Value
|
||||
Schnelleres Onboarding, weniger Fehlkonfigurationen und klare Betriebsvorgaenge.
|
||||
|
||||
## Mission Statement
|
||||
Stelle eine professionelle, konsistente und vollstaendige Doku bereit, die sich direkt am
|
||||
Code und den Skripten orientiert.
|
||||
|
||||
## Business Value & Metriken
|
||||
- Onboarding ohne Rueckfragen fuer neue Entwickler.
|
||||
- Betriebssicherheit durch klare Runbooks.
|
||||
- Erfolgsmetrik: 0 offene Doku-Abweichungen im Audit.
|
||||
|
||||
## In-Scope (Kiddo Team)
|
||||
- Neue Doku-Struktur und konsistente Inhalte.
|
||||
- README auf Einsteigerpfad + Links auf neue Dokus.
|
||||
- Altdokumente archivieren, nicht loeschen.
|
||||
|
||||
## Out-of-Scope
|
||||
- Funktionale Code-Aenderungen (nur Doku).
|
||||
- Neue Automatisierungen/CI-Pipelines.
|
||||
|
||||
## High-Level Akzeptanzkriterien
|
||||
- README erfuellt definierte Anforderungen.
|
||||
- Doku-Struktur gem. Ziel (GETTING_STARTED/USAGE/CONFIGURATION/ARCHITECTURE/DEVELOPMENT/DEPLOYMENT/FAQ/TROUBLESHOOTING).
|
||||
- Altdoku in docs/_archive/ mit Archiv-Header.
|
||||
- Interne Links funktionieren und keine inhaltlichen Widersprueche.
|
||||
|
||||
## Technische Constraints & Risiken
|
||||
- Keine spekulativen Inhalte; nur dokumentieren, was im Repo belegt ist.
|
||||
- Sprachvorgabe: Deutsch, ASCII wo moeglich.
|
||||
|
||||
## Zugeordnete User Stories
|
||||
- US_000040: Doku-Struktur und Inhalte erstellen
|
||||
19
project-management/requirements/stories/US_000040.md
Normal file
19
project-management/requirements/stories/US_000040.md
Normal file
@ -0,0 +1,19 @@
|
||||
ID: US_000040 | Version: 0.2.1 | Status: Done
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# US_000040: Doku-Struktur und Inhalte erstellen
|
||||
|
||||
Als Nutzer moechte ich eine klare, vollstaendige Dokumentation, damit Einsteiger,
|
||||
Admins und Entwickler das System ohne Rueckfragen verstehen und betreiben koennen.
|
||||
|
||||
## Akzeptanzkriterien
|
||||
- Given die Zielstruktur ist definiert
|
||||
- When die Dokumentation erstellt wird
|
||||
- Then existieren alle Ziel-Dokumente mit korrekten Inhalten
|
||||
- And README enthaelt Quickstart, Zielgruppen, Features und Links
|
||||
- And alle Inhalte basieren auf Code/Skripten ohne Spekulation
|
||||
- And Altdokumente sind archiviert statt geloescht
|
||||
|
||||
## Task-Platzhalter
|
||||
- TASK_000043: Neue Doku-Dateien erstellen und verlinken
|
||||
- TASK_000044: Altdoku archivieren und Hinweise einfuegen
|
||||
20
project-management/requirements/tasks/TASK_000043.md
Normal file
20
project-management/requirements/tasks/TASK_000043.md
Normal file
@ -0,0 +1,20 @@
|
||||
ID: TASK_000043 | Version: 0.2.1 | Status: Done
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# TASK_000043: Neue Doku-Dateien erstellen und verlinken
|
||||
|
||||
## Outcome
|
||||
Komplette neue Doku-Struktur mit konsistenten Inhalten und funktionierenden Links.
|
||||
|
||||
## Story-Bezug
|
||||
US_000040
|
||||
|
||||
## Beschreibung
|
||||
- README neu strukturieren und Links auf Doku-Dateien setzen.
|
||||
- GETTING_STARTED/USAGE/CONFIGURATION/ARCHITECTURE/DEVELOPMENT/DEPLOYMENT/FAQ/TROUBLESHOOTING erstellen.
|
||||
- Inhalte auf Basis von Code/Skripten verifizieren.
|
||||
|
||||
## Definition of Done (DoD)
|
||||
- Alle Ziel-Dokumente existieren und sind vollstaendig.
|
||||
- Keine widerspruechlichen Inhalte gegenueber Code.
|
||||
- Interne Links funktionieren.
|
||||
20
project-management/requirements/tasks/TASK_000044.md
Normal file
20
project-management/requirements/tasks/TASK_000044.md
Normal file
@ -0,0 +1,20 @@
|
||||
ID: TASK_000044 | Version: 0.2.1 | Status: Done
|
||||
By: Codex (GPT-5)
|
||||
|
||||
# TASK_000044: Altdoku archivieren
|
||||
|
||||
## Outcome
|
||||
Veraltete Dokumente sind in docs/_archive/ verschoben und klar als archiviert markiert.
|
||||
|
||||
## Story-Bezug
|
||||
US_000040
|
||||
|
||||
## Beschreibung
|
||||
- Altdokumente identifizieren.
|
||||
- In docs/_archive/ verschieben.
|
||||
- Archiv-Header einfuegen.
|
||||
|
||||
## Definition of Done (DoD)
|
||||
- Keine Loeschung alter Dokus.
|
||||
- Archiv-Header vorhanden.
|
||||
- Neue Dokus referenzieren nur aktuelle Dateien.
|
||||
Reference in New Issue
Block a user