docs: add project management structure
This commit is contained in:
62
project-management/feedback/THEME_WATCHTOWER.md
Normal file
62
project-management/feedback/THEME_WATCHTOWER.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Theme: Watchtower (Sci-Fi / Dark)
|
||||
|
||||
Dieses Theme basiert auf der `minecraft-watchtower` UI. Es ist ein **High-Contrast Dark Mode** mit Neon-Akzenten, ausgelegt auf technische Dashboards und "Immersive UIs".
|
||||
|
||||
## Verwendung
|
||||
|
||||
Binde statt `tokens.css` die Datei `tokens_watchtower.css` ein und ergänze `theme_watchtower.css`.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="design/tokens_watchtower.css">
|
||||
<link rel="stylesheet" href="design/components.css"> <!-- Standard Components -->
|
||||
<link rel="stylesheet" href="design/theme_watchtower.css"> <!-- Theme Overrides -->
|
||||
```
|
||||
|
||||
Zusätzlich sollte die Klasse `.bg-noise` direkt nach dem `<body>` Tag eingefügt werden:
|
||||
```html
|
||||
<body>
|
||||
<div class="bg-noise"></div>
|
||||
...
|
||||
</body>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TUI (Terminal User Interface) Adaption
|
||||
|
||||
Da dieses Design oft in CLI-Tools oder TUIs verwendet wird, gelten folgende Mappings für Terminals (16/256 Farben).
|
||||
|
||||
### Farb-Palette
|
||||
|
||||
| Rolle | CSS Variable | ANSI Color (16) | ANSI Code | Hex Fallback |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **Background** | `--bg-dark` | Black | `\e[40m` | `#0b0f14` |
|
||||
| **Text** | `--text` | White (Bright) | `\e[97m` | `#e6edf5` |
|
||||
| **Muted Text** | `--text-dim` | Cyan (Dim) | `\e[36m` | `#9aa7b8` |
|
||||
| **Accent** | `--accent` | Cyan (Bright) | `\e[96m` | `#00e08f` |
|
||||
| **Success** | `--success` | Green (Bright) | `\e[92m` | `#00e08f` (Teal) |
|
||||
| **Warning** | `--warning` | Yellow (Bright) | `\e[93m` | `#ffb454` |
|
||||
| **Error** | `--danger` | Red (Bright) | `\e[91m` | `#ff5e5e` |
|
||||
|
||||
### Block-Elemente & Rahmen
|
||||
|
||||
Für TUI-Rahmen nutzen wir "Heavy" oder "Double" Lines, um den technischen Look zu imitieren.
|
||||
|
||||
* **Box Border:** `═` (Double Horizontal), `║` (Double Vertical), `╔ ╗ ╚ ╝` (Corners)
|
||||
* **Progress Bar:** `█` (Full Block) für den Füllstand, `░` (Light Shade) für den Hintergrund.
|
||||
|
||||
### Beispiel (Charm / Bubble Tea - Go)
|
||||
|
||||
```go
|
||||
var (
|
||||
ColorAccent = lipgloss.Color("#00e08f")
|
||||
ColorBg = lipgloss.Color("#0b0f14")
|
||||
ColorText = lipgloss.Color("#e6edf5")
|
||||
|
||||
StyleCard = lipgloss.NewStyle().
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(ColorAccent).
|
||||
Padding(1, 2).
|
||||
Background(ColorBg)
|
||||
)
|
||||
```
|
||||
86
project-management/feedback/oidc-integration-userstories.md
Normal file
86
project-management/feedback/oidc-integration-userstories.md
Normal file
@ -0,0 +1,86 @@
|
||||
# OIDC Integration: OICD (IdP) + Kiddo (Clients)
|
||||
|
||||
## Herangehensweise und Gedankengang
|
||||
|
||||
Ziel war es, beide Projekte so zu verbinden, dass Kiddo OICD als OIDC-Provider nutzt, ohne Codeaenderungen vorzunehmen. Wir haben die vorhandenen Konfigurationspfade, DCR-Faehigkeiten und OIDC-Endpunkte geprueft und daraus die minimalen, operationalen Schritte abgeleitet.
|
||||
|
||||
Leitfragen, die wir dabei beantwortet haben:
|
||||
- Welche Konfiguration erwartet Kiddo fuer OIDC (Issuer, Client, Redirect, Cookies)?
|
||||
- Welche OIDC-Funktionen liefert OICD (Discovery, JWKS, DCR, Admin-UI)?
|
||||
- Wo liegen die harten OIDC-Anforderungen (Issuer-Match, exakte Redirect-URIs, TLS-Vertrauen)?
|
||||
- Wie kann die Registrierung skalieren, wenn Geraete und Redirects erst spaeter feststehen?
|
||||
|
||||
### OICD (IdP) - Herangehensweise
|
||||
Wir haben in OICD geprueft, welche OIDC-Endpunkte vorhanden sind (Discovery, JWKS, Token, DCR) und wie der Issuer erzeugt wird. Entscheidend ist, dass der Issuer exakt der externen URL entspricht, unter der OICD erreichbar ist. Zudem gibt es eine Admin-Funktion zur Erzeugung von Initial-Access-Tokens, die den DCR-Flow ermoeglichen. Daraus folgt: Stabiler Issuer (prod/dev), TLS trust, und ein standardisierter Weg zur Token-Erzeugung fuer DCR.
|
||||
|
||||
### Kiddo (Client) - Herangehensweise
|
||||
Wir haben in Kiddo geprueft, welche Umgebungsvariablen fuer OIDC benoetigt werden und wie die Claims interpretiert werden. Kiddo validiert ID-Tokens gegen den Issuer und JWKS und benoetigt exakte Redirect-URIs. Da die Device-Hosts dynamisch sind, ist DCR der beste Weg, pro Geraet eigene Clients zu registrieren, sobald die finale URL bekannt ist. Daraus folgen: pro Geraet DCR, danach Env-Setup, optional Allowlist fuer OIDC-User.
|
||||
|
||||
Aus diesen Punkten ergab sich der Weg: OICD stellt stabile Issuer-URLs bereit, Redirect-URIs muessen konkret registriert werden, und DCR (mit Initial-Access-Token) ist der beste Weg, um pro Geraet eigene Clients dynamisch zu erzeugen. Daraus wurden die Stories, Akzeptanzkriterien und das Provisioning-Kommando abgeleitet.
|
||||
|
||||
## Epic: OIDC-Login fuer Kiddo (Client-Seite)
|
||||
|
||||
### Story 1: Zentrales Provisioning registriert pro Geraet einen OIDC-Client
|
||||
Als Provisioner moechte ich pro Geraet einen OIDC-Client via DCR anlegen, damit jedes Geraet einen eigenen Client-ID/Secret hat.
|
||||
|
||||
Acceptance Criteria:
|
||||
- DCR-Call mit `SKD_OIDC_ISSUER`, `SKD_OIDC_REDIRECT_URI` und `OIDC_INITIAL_ACCESS_TOKEN` erzeugt `client_id` und `client_secret`.
|
||||
- Redirect-URI ist exakt `https://<device-host>[:port]/login/oidc/callback` (keine Wildcards).
|
||||
|
||||
Referenzen:
|
||||
- `kiddo/scripts/register_oidc_client.sh`
|
||||
|
||||
### Story 2: Kiddo-Geraet ist per OIDC konfiguriert
|
||||
Als Geraetebetreiber moechte ich ein Kiddo-Geraet so konfigurieren, dass es sich ueber OICD authentifiziert.
|
||||
|
||||
Acceptance Criteria:
|
||||
- `SKD_AUTH_MODE=oidc` ist gesetzt.
|
||||
- `SKD_OIDC_ISSUER`, `SKD_OIDC_CLIENT_ID`, `SKD_OIDC_CLIENT_SECRET`, `SKD_OIDC_REDIRECT_URI` sind gesetzt.
|
||||
- Bei HTTPS ist `SKD_SESSION_COOKIE_SECURE=true`.
|
||||
|
||||
Referenzen:
|
||||
- `kiddo/env.example`
|
||||
- `kiddo/README.md`
|
||||
- `kiddo/backend/settings.py`
|
||||
|
||||
### Story 3: Zugriffskontrolle auf OIDC-Login
|
||||
Als Betreiber moechte ich steuern, welche Benutzer sich via OIDC anmelden duerfen.
|
||||
|
||||
Acceptance Criteria:
|
||||
- `SKD_AUTH_ALLOWED_USERS` schraenkt Zugriff ein, basierend auf `preferred_username` oder `email` oder `sub` aus dem ID-Token.
|
||||
|
||||
Referenzen:
|
||||
- `kiddo/backend/oidc.py`
|
||||
- `kiddo/backend/auth.py`
|
||||
|
||||
### Story 4: Betrieb mit wechselnden Geraeten (DCR-Flow)
|
||||
Als Betreiber moechte ich neue Geraete spaeter onboarden koennen, ohne OICD manuell zu konfigurieren.
|
||||
|
||||
Acceptance Criteria:
|
||||
- DCR-Prozess ist dokumentiert und kann pro Geraet wiederholt werden.
|
||||
- Bei Host/Port-Aenderung erfolgt Neuregistrierung (neuer Client/Secret).
|
||||
|
||||
Referenzen:
|
||||
- `kiddo/scripts/register_oidc_client.sh`
|
||||
|
||||
## DCR Kommando (zentral, pro Geraet)
|
||||
|
||||
Prod:
|
||||
```bash
|
||||
export SKD_OIDC_ISSUER="https://auth.wlkns.org"
|
||||
export SKD_OIDC_REDIRECT_URI="https://<device-host>[:port]/login/oidc/callback"
|
||||
export OIDC_INITIAL_ACCESS_TOKEN="<initial-access-token>"
|
||||
/home/stephan/applications/wlkns/kiddo/scripts/register_oidc_client.sh
|
||||
```
|
||||
|
||||
Dev:
|
||||
```bash
|
||||
export SKD_OIDC_ISSUER="https://dev.wlkns.org"
|
||||
export SKD_OIDC_REDIRECT_URI="https://<device-host>[:port]/login/oidc/callback"
|
||||
export OIDC_INITIAL_ACCESS_TOKEN="<initial-access-token>"
|
||||
/home/stephan/applications/wlkns/kiddo/scripts/register_oidc_client.sh
|
||||
```
|
||||
|
||||
Hinweise:
|
||||
- Redirect-URIs muessen exakt registriert sein (keine Wildcards).
|
||||
- Bei Host/Port-Aenderung: neu registrieren und neue Client-Credentials setzen.
|
||||
135
project-management/feedback/theme_watchtower.css
Normal file
135
project-management/feedback/theme_watchtower.css
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
wlkns-dev-standards
|
||||
Component Theme: Watchtower
|
||||
Adapts standard components to the Sci-Fi/Dark aesthetic.
|
||||
*/
|
||||
|
||||
/* =========================
|
||||
Inputs & Forms
|
||||
========================= */
|
||||
input, select, textarea {
|
||||
background: rgba(10, 14, 20, 0.6) !important;
|
||||
border-color: rgba(255, 255, 255, 0.15) !important;
|
||||
color: var(--color-text) !important;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
input:focus, select:focus, textarea:focus {
|
||||
border-color: var(--color-accent) !important;
|
||||
box-shadow: 0 0 15px var(--wt-glow);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
Buttons
|
||||
========================= */
|
||||
.button {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 600;
|
||||
border-radius: 999px !important; /* Pill shape */
|
||||
}
|
||||
|
||||
.button.primary {
|
||||
box-shadow: 0 0 15px var(--wt-glow);
|
||||
color: #081015; /* Dark text on bright accent */
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
border-color: var(--color-text);
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
.button.danger {
|
||||
box-shadow: 0 0 10px rgba(255, 94, 94, 0.4);
|
||||
color: #1b0d0d;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
Tables
|
||||
========================= */
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 4px; /* Space between rows */
|
||||
}
|
||||
|
||||
thead th {
|
||||
border-bottom: 1px solid var(--color-accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-accent);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
tbody td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
tbody td:first-child {
|
||||
border-top-left-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
tbody td:last-child {
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
Alerts & Toasts
|
||||
========================= */
|
||||
.alert, .toast {
|
||||
background: rgba(10, 14, 20, 0.95);
|
||||
backdrop-filter: blur(4px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.alert.success, .toast.success {
|
||||
border-color: var(--color-success);
|
||||
box-shadow: 0 0 10px rgba(0, 224, 143, 0.2);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.alert.warning, .toast.warning {
|
||||
border-color: var(--color-warning);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.alert.error, .toast.error {
|
||||
border-color: var(--color-error);
|
||||
box-shadow: 0 0 10px rgba(255, 94, 94, 0.2);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
Pagination
|
||||
========================= */
|
||||
.pagination .page {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.pagination .page.active {
|
||||
background: var(--color-accent);
|
||||
color: #081015;
|
||||
border-color: var(--color-accent);
|
||||
box-shadow: 0 0 10px var(--wt-glow);
|
||||
}
|
||||
69
project-management/feedback/tokens_watchtower.css
Normal file
69
project-management/feedback/tokens_watchtower.css
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
wlkns-dev-standards
|
||||
Theme: Watchtower (Dark Glow)
|
||||
Version: v1.0
|
||||
Based on: minecraft-watchtower UI
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* =========================
|
||||
Core Palette (Watchtower)
|
||||
========================= */
|
||||
--wt-bg-dark: #0b0f14;
|
||||
--wt-bg-panel: #10151d;
|
||||
--wt-accent: #00e08f;
|
||||
--wt-warning: #ffb454;
|
||||
--wt-danger: #ff5e5e;
|
||||
--wt-text: #e6edf5;
|
||||
--wt-text-dim: #9aa7b8;
|
||||
--wt-glow: rgba(0, 224, 143, 0.35);
|
||||
|
||||
/* =========================
|
||||
Mapping -> Standard Tokens
|
||||
========================= */
|
||||
|
||||
/* Farben – Identity & Status */
|
||||
--color-primary: #1a2230; /* Deep Blue/Grey from Gradient */
|
||||
--color-accent: var(--wt-accent);
|
||||
|
||||
--color-success: var(--wt-accent); /* Watchtower uses accent as success */
|
||||
--color-warning: var(--wt-warning);
|
||||
--color-error: var(--wt-danger);
|
||||
|
||||
/* Farben – Neutrals (Dark Mode Override) */
|
||||
--color-bg: var(--wt-bg-dark);
|
||||
--color-surface: var(--wt-bg-panel);
|
||||
--color-border: rgba(255, 255, 255, 0.08); /* Subtle white border */
|
||||
|
||||
--color-text: var(--wt-text);
|
||||
--color-text-muted: var(--wt-text-dim);
|
||||
|
||||
/* Typografie (Optional: Falls Space Grotesk geladen wird) */
|
||||
--font-ui: 'Space Grotesk', 'Inter', system-ui, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', monospace;
|
||||
|
||||
/* Radius & Spacing */
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 12px; /* Watchtower uses larger radii */
|
||||
--radius-lg: 16px;
|
||||
|
||||
/* Shadows becomes Glows in this theme */
|
||||
--shadow-sm: 0 0 10px rgba(0,0,0,0.5);
|
||||
--shadow-glow: 0 0 15px var(--wt-glow);
|
||||
}
|
||||
|
||||
/* Global Theme Overrides */
|
||||
body {
|
||||
background: radial-gradient(circle at top, #1a2230 0%, #0b0f14 55%), linear-gradient(135deg, #0b0f14, #101623 60%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Noise Texture helper class */
|
||||
.bg-noise {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160' viewBox='0 0 160 160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
|
||||
mix-blend-mode: soft-light;
|
||||
z-index: -1;
|
||||
}
|
||||
Reference in New Issue
Block a user