// Persönlich · eigenständig · laufend
Portfolio
AbstractDiese Seite hier. Die Oberfläche ist einem Code-Editor nachgebaut, mit Tabs, Explorer, Terminal und Kommandopalette. Öffentlich sind Skills und Projekte, hinter einem Passwort liegen Noten und Lebenslauf. Alles von Hand geschrieben, ohne Framework und ohne eine einzige externe Abhängigkeit.
Drin steckt unter anderem eine Tabverwaltung mit Tastatursteuerung, ein Explorer, der auf dem Handy zur Schublade wird, sechs Farbdesigns und ein eigenes Druck-Stylesheet. Der Login geht an eine Serverless Function, die das Passwort gegen einen gesalzenen scrypt-Hash prüft und ein Token mit vier Stunden Laufzeit zurückgibt. Noten und Lebenslauf liegen auf dem Server und stehen nie im ausgelieferten HTML. Wer den Quelltext im Browser öffnet, findet dort nichts.
- Meine Rolle
- Alleine umgesetzt: Konzept, Gestaltung, Frontend und Backend
- Besonderheit
- Kein Framework, keine npm-Abhängigkeiten. Was im Browser ankommt, habe ich geschrieben.
HTMLCSSJavaScriptNode.jsVercel
// Persönlich · eigenständig · eigenes Skript
Wallpaper-Switcher mit automatischem Farbschema
AbstractEin Bash-Skript, das aus meinem Hintergrundbild ein Farbschema errechnet und es auf das ganze System anwendet. Ein Bildwechsel färbt damit Fensterrahmen, Leiste, Menü und Terminal automatisch mit um, ohne dass ich eine Konfigurationsdatei anfassen muss.
Das Skript verkettet vier Werkzeuge: waypaper setzt das Bild, matugen leitet die Farbpalette daraus ab, rofi ist das Auswahlmenü und waybar die Statusleiste. Danach schreibt es die Konfigurationsdateien der beteiligten Programme neu und startet sie kontrolliert neu. Der knifflige Teil waren nicht die Farben, sondern die Reihenfolge: Lädt ein Programm zu früh neu, liest es noch die alte Palette.
- Meine Rolle
- Alleine geschrieben
- Gelernt
- Bash, Kommandozeilenwerkzeuge verketten, Konfigurationsdateien und Prozesse unter Linux
BashMatugenWaypaperRofiWaybarHyprland
// Persönlich · eigenständig
Arch Linux + Hyprland Dual-Boot
AbstractEin selbst aufgesetztes Dual-Boot-System auf meinem ThinkPad: Windows 11 für den Alltag, Arch Linux mit dem Window Manager Hyprland fürs Programmieren. Eingerichtet, weil ich wissen wollte, wie ein Linux-System unter der Oberfläche zusammengesetzt ist.
Arch über archinstall installiert und Hyprland von Grund auf konfiguriert. Bei Hyprland gibt es keine fertige Oberfläche: Jede Taste und jedes Fensterverhalten steht in einer Konfigurationsdatei. Dazu Alacritty als Terminal, die Fish-Shell, waybar als Statusleiste, fuzzel als Programmstarter und Nautilus als Dateimanager. Das Abmeldemenü ist kein fertiges Programm, sondern ein rofi-Skript, das aus der Leiste heraus aufgerufen wird. Aus diesem Setup ist auch der Wallpaper-Switcher entstanden, der inzwischen ein eigenes Repository hat.
- Meine Rolle
- Alleine umgesetzt
- Gelernt
- Partitionierung und Bootloader, Konfigurationsverwaltung, Fehlersuche ohne grafische Oberfläche
Arch LinuxHyprlandAlacrittyFishWaybarNautilusDual-Boot
// Persönlich · eigenständig
Konsolen-Modding: Switch & 3DS
AbstractAuf meiner eigenen Nintendo Switch und meinem 3DS eine Custom Firmware eingerichtet. Mich hat interessiert, wie ein Bootvorgang abläuft und an welcher Stelle ein System seine Signaturen prüft.
Bei der Switch über ein RCM-Jig im Recovery-Modus den Payload Hekate geladen und ein EmuMMC aufgesetzt, also eine abgetrennte Systemkopie, damit das Originalsystem unangetastet bleibt. Das ging nur, weil die Firmware noch nicht gepatcht war. Auf dem 3DS läuft Luma3DS. Beide Geräte gehören mir, und es ging um die Boot-Kette, nicht um Inhalte.
- Meine Rolle
- Alleine umgesetzt
- Gelernt
- Bootvorgang, Recovery-Modi, Signaturprüfung und der Umgang mit englischer Fachdokumentation
RCMHekateEmuMMCLuma3DS
// Schulprojekt · Modul 210 · eigenständig
URL-Shortener mit GitOps-Kette
AbstractEin URL-Shortener aus zwei NestJS-Diensten und einer MariaDB, der nicht nur läuft, sondern auch ausgeliefert wird: containerisiert, über eine CI-Pipeline gebaut und von ArgoCD automatisch in einen Kubernetes-Cluster deployt. Das Interessante war weniger das Kürzen von Links als der Weg vom Commit bis in den laufenden Cluster.
Die Anwendung. shorty nimmt lange URLs entgegen, gibt einen Kurzcode zurück und leitet beim Aufruf per 302 weiter. keeper ist der einzige Dienst, der die Datenbank sieht, und ist per API-Key geschützt. Von aussen erreichbar ist nur shorty über den Ingress, keeper hängt als ClusterIP dahinter. Ein Angreifer, der von aussen kommt, hat also keinen direkten Weg zur Datenbank.
Der Weg in den Cluster. Beide Dienste laufen in Containern, gebaut über eine GitLab-Pipeline. Die Kubernetes-Manifeste liegen bewusst in einem zweiten Repo: Anwendungscode und gewünschter Cluster-Zustand sind getrennt. ArgoCD beobachtet dieses GitOps-Repo und gleicht den Cluster automatisch an. Ein Deployment ist damit ein Commit, kein kubectl-Befehl von Hand. Wer den Zustand ändern will, muss durch Git, und dort ist jede Änderung nachvollziehbar.
Dazu kam schriftliche Arbeit, die ich sonst nirgends gemacht habe: ein Rollenkonzept mit IAM und RBAC, ein Sicherheitskonzept und eine Kostenanalyse für einen Betrieb in AWS. Alle drei theoretisch, aber sie haben mich zum ersten Mal darüber nachdenken lassen, was der Betrieb einer Anwendung tatsächlich kostet und wer worauf zugreifen darf.
- Meine Rolle
- Alleine umgesetzt. Der Kurs stellte ein leeres Grundgerüst mit den ersten sechs Commits, alles ab der Containerisierung stammt von mir: 17 der 23 Commits.
- Gelernt
-
- Warum man Anwendungscode und Deployment-Zustand trennt
- Dass Container zur Sicherheit gehören, nicht nur zum Ausliefern, etwa wenn sie nicht als root laufen
- Einen Dienst so schneiden, dass nur einer nach aussen zeigt
- Dass ein Deployment reproduzierbar sein muss, sonst ist es Glückssache
NestJSTypeScriptMariaDBDockerGitLab CIKubernetesArgoCD
// Schulprojekt · Modul 335 · Dreierteam
Askel
AbstractAskel heisst auf Finnisch «Schritt» und ist eine mobile App, die eine Fahrt per GPS aufzeichnet: Position, Höhe und Geschwindigkeit. Routen lassen sich speichern, benennen, farblich markieren und auf einer Karte anzeigen. Alle Daten bleiben auf dem Gerät, ein Backend gibt es nicht.
Umgesetzt mit React Native und Expo. Die Standortdaten kommen über expo-location, die Aufzeichnung läuft während der Fahrt weiter und die Routen werden lokal gespeichert. Dass es kein Backend gibt, war eine bewusste Entscheidung: Bewegungsprofile sind heikle Daten, und was das Gerät nie verlässt, kann auch nicht abfliessen.
- Meine Rolle
- Co-Developer im Dreierteam. Von mir stammen unter anderem:
- Umgesetzt
-
- Dauerhaftes Speichern der aufgezeichneten Routen auf dem Gerät
- GPS-Aufzeichnung mit Distanzberechnung (
lib/distance.ts)
- Einstellungsbereich der App samt Formatierung der Messwerte
- Farbauswahl beim Speichern einer Route
- Schaltfläche zum Zentrieren der Karte auf die eigene Position
- Automatisierte Tests für Distanzberechnung, Formatierung und Speicherung
React NativeExpoTypeScriptexpo-locationJest
// Schulprojekt · Modul 322 · eigenständig
Mein Rezeptbuch
AbstractEine Android-App zum Verwalten von Rezepten, geschrieben in C# mit .NET MAUI. Vier Bereiche über eine Tab-Leiste: die Sammlung mit Suche und Vegetarisch-Filter, eine Eingabemaske mit Validierung, eine FAQ-Seite und eine Info-Seite. Auftrag im Modul 322 für einen fiktiven Kunden, von der Oberfläche bis zur Logik allein umgesetzt.
Aufbau. Die App folgt MVVM, also der Trennung von Oberfläche und Logik. Die Views sind XAML-Dateien und enthalten keine Logik, der Zustand liegt in den ViewModels, und ein RezeptService als Singleton hält die Rezepte. Eine BaseViewModel mit SetProperty und INotifyPropertyChanged sorgt dafür, dass die Oberfläche jede Änderung von selbst mitbekommt: Ich setze eine Eigenschaft im Code, und das Feld auf dem Bildschirm aktualisiert sich, ohne dass ich es anfasse. Das war der Punkt, an dem Datenbindung für mich zum ersten Mal Sinn ergeben hat.
Validierung. Titel und Beschreibung sind Pflichtfelder und werden beim Tippen geprüft, nicht erst beim Speichern. Der Titel braucht mindestens drei Zeichen, die Beschreibung zehn; die Meldung steht direkt am Feld statt in einem Dialog, der erst nach dem Absenden aufgeht. Die Navigation läuft über die Shell mit benannten Routen, das Detail einer Karte wird als RezeptDetailPage?RezeptId=… aufgerufen.
Was sie nicht kann. Die Rezepte liegen nur im Arbeitsspeicher. Schliesst man die App, sind die selbst angelegten wieder weg und nur die vier Beispielrezepte da. Bilder gibt es ebenfalls keine, nur Platzhalter. Beides war im Auftrag nicht verlangt — es ging um Oberfläche und Datenbindung — aber es ist die erste Sache, die ich nachziehen würde: eine SQLite-Datei hinter denselben RezeptService hängen, damit der Rest der App unverändert bleibt.
- Meine Rolle
- Alleine umgesetzt: Oberfläche, ViewModels und Datenhaltung
- Gelernt
-
- C# in einem echten Projekt statt in Übungsaufgaben
- MVVM: warum die Oberfläche nichts über die Datenhaltung wissen soll
- Datenbindung mit
INotifyPropertyChanged statt Felder von Hand nachzuführen
- Oberflächen in XAML aufbauen und über Styles zentral gestalten
- Eingaben prüfen, während getippt wird, nicht erst beim Absenden
C#.NET MAUIXAMLMVVMAndroid
// Schulprojekt · überbetrieblicher Kurs (ÜK) · Dreierteam
Kobui
AbstractKobui ist eine Web-App, die ein lokal laufendes Sprachmodell (KoboldCpp) an ein eigenes React-Frontend anbindet. Gedacht als datenschutzfreundliche Alternative zu Cloud-Diensten: Weil das Modell auf dem eigenen Rechner läuft, verlässt keine Eingabe das Gerät.
Frontend mit TypeScript, React und Vite, gestaltet über CSS Modules. Die Anwendung spricht die lokale Schnittstelle von KoboldCpp an und bringt eine eigene Anleitung mit, damit man sie auch ohne Vorwissen aufsetzen kann.
- Meine Rolle
- Organisation und Gestaltung im Dreierteam. Ich habe Aufgaben verteilt, den Zeitplan im Blick behalten und bin eingesprungen, wo es gegen Ende knapp wurde.
- Beigetragen
- Installationsanleitung für das lokale Backend, Ergänzung alternativer Modelle und Korrekturen an der Bedienoberfläche
- Gelernt
- Wie viel Abstimmung ein Dreierteam braucht. Und dass ein realistischer Zeitplan mehr wert ist als ein ehrgeiziger.
TypeScriptReactViteCSS Modules
// Schulprojekt · über drei Module gewachsen · laufend
Redis-Webshop
AbstractEin Webshop, der seine Daten nicht in einer Tabellendatenbank hält, sondern komplett in Redis. Entstanden ist er im NoSQL-Modul als ziemlich leeres Grundgerüst. Seither ist er mein Übungsprojekt: In jedem folgenden Modul, in dem es gepasst hat, habe ich ihn um das erweitert, was gerade dran war.
NoSQL-Modul: das Datenmodell. Jeder Datensatz liegt als Hash unter einem sprechenden Schlüssel, also product:12, user:3, cart:3. Redis kennt kein WHERE, deshalb braucht jede Suche einen eigenen Index. Die Kategorien liegen als Mengen unter idx:kategorie:* und enthalten die Produkt-IDs. Genau das war der Lerneffekt: In SQL wäre es eine einzige Abfrage gewesen, hier muss ich den Index beim Speichern von Hand mitschreiben. Vergesse ich das an einer Stelle, findet die Suche das Produkt nie wieder.
Docker-Modul: das Ausliefern. Anwendung und Datenbank starten seither zusammen über Docker Compose, damit der Shop auch auf einem fremden Rechner ohne Einrichtung läuft. Aus derselben Zeit stammt eine Kleinigkeit, über die ich mich immer noch freue: Die Verbindung sucht sich ihren Port selbst. Steht keine Umgebungsvariable, probiert sie 6379 und 6380 durch. In der Schule lief Redis auf einem anderen Port als zuhause, und statt die Datei jedes Mal zu ändern, klärt das Programm es eben selbst.
DevOps- und Azure-Modul: der Betrieb. Zuletzt ging es darum, die Anwendung nicht nur lokal laufen zu lassen, sondern auf einem Server zu betreiben und im Blick zu behalten.
Heute kann der Shop: Anmeldung mit Rollen für Admins und Kundschaft, Produkte anlegen, bearbeiten und löschen, einen Warenkorb pro Benutzer und Skripte, die alles mit Testdaten füllen.
- Meine Rolle
- Eigenes Repository, von mir umgesetzt und über die Module hinweg weiterentwickelt
- Warum immer dasselbe Projekt
- Ein neues Thema an bekanntem Code zu lernen zeigt den Unterschied deutlicher als ein frisches Beispielprojekt. Nebenbei sieht man an der Historie, wie sich derselbe Shop über ein Jahr verändert hat.
- Gelernt
-
- Datenmodellierung ohne Tabellen: der Schlüssel ist die Struktur
- Warum eine Key-Value-Datenbank schnell ist und was man dafür aufgibt
- Indizes selbst pflegen, statt sie von der Datenbank zu bekommen
- Mehrere Dienste gemeinsam starten und ausliefern
- Dass eine Anwendung auf einem Server andere Fragen stellt als eine, die nur lokal läuft
PythonFlaskRedisDocker ComposeJinja2Azure
// Schulprojekt · 1. Lehrjahr · eigenständig · mein erstes Projekt
Erste eigene Website
AbstractEine Info-Seite über mich mit fünf Unterseiten: Home, Über mich, Freizeit, Ausbildung und Kontakt. Reines HTML und CSS, ohne JavaScript, von Hand geschrieben und über FTP auf den Klassenserver geladen. Das war im ersten Lehrjahr meine erste Website überhaupt.
Inhaltlich ging es um mich selbst, technisch um die Grundlagen: eine gemeinsame Navigation auf jeder Seite, ein durchgehendes Layout, eingebundene Bilder und eine Quellenseite für alles, was nicht von mir stammt. Die Seite liegt bis heute unverändert auf dem Schulserver; eine Kopie davon liegt als Unterseite hier, damit sie erreichbar bleibt, wenn der Schulserver es irgendwann nicht mehr ist.
Ich zeige sie bewusst im Originalzustand. Die Rechtschreibung ist stellenweise daneben, jede Unterseite wiederholt dieselbe Navigation als Kopie, und es gibt kein Layout fürs Handy. Genau daran sehe ich den Abstand zu dem, was ich heute baue: Das Portfolio, auf dem Sie gerade sind, ist dasselbe Vorhaben — eine Seite über mich — nur ein Jahr später.
- Meine Rolle
- Alleine umgesetzt: Inhalt, Layout und Umsetzung
- Gelernt
-
- HTML-Grundgerüst, Verlinkung zwischen mehreren Seiten und Einbinden von Bildern
- CSS auslagern, statt jede Seite einzeln zu gestalten
- Dateien per FTP auf einen Webserver bringen
- Dass Quellen anzugeben dazugehört, auch bei Bildern
- Was ich heute anders mache
- Navigation nicht mehr in jede Datei kopieren, Layout von Anfang an fürs Handy mitdenken und den Text vor dem Veröffentlichen gegenlesen
HTMLCSSFTP