{"id":49005987,"url":"https://github.com/malziland/malzime","last_synced_at":"2026-06-06T23:01:08.570Z","repository":{"id":338817258,"uuid":"1159277524","full_name":"malziland/malzime","owner":"malziland","description":"Was KI aus deinem Foto liest — Workshop-Tool für Medienkompetenz und Datenschutz-Sensibilisierung","archived":false,"fork":false,"pushed_at":"2026-04-01T04:53:17.000Z","size":4868,"stargazers_count":1,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-01T07:46:28.344Z","etag":null,"topics":["datenschutz","education","firebase","gemini","ki","medienkompetenz","privacy","safer-internet","vanilla-js","workshop"],"latest_commit_sha":null,"homepage":"https://malzi.me","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/malziland.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"malziland","buy_me_a_coffee":"malzime"}},"created_at":"2026-02-16T14:37:56.000Z","updated_at":"2026-04-01T04:53:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/malziland/malzime","commit_stats":null,"previous_names":["malziland/malzime"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/malziland/malzime","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malziland%2Fmalzime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malziland%2Fmalzime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malziland%2Fmalzime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malziland%2Fmalzime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/malziland","download_url":"https://codeload.github.com/malziland/malzime/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malziland%2Fmalzime/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31982830,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T17:30:12.329Z","status":"ssl_error","status_checked_at":"2026-04-18T17:29:59.069Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["datenschutz","education","firebase","gemini","ki","medienkompetenz","privacy","safer-internet","vanilla-js","workshop"],"created_at":"2026-04-18T20:11:28.018Z","updated_at":"2026-04-18T20:11:28.546Z","avatar_url":"https://github.com/malziland.png","language":"JavaScript","funding_links":["https://github.com/sponsors/malziland","https://buymeacoffee.com/malzime"],"categories":[],"sub_categories":[],"readme":"# malziME — Was KI aus deinem Foto liest\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue)](https://github.com/malziland/malzime/blob/main/LICENSE)\n[![Firebase Hosting](https://img.shields.io/badge/Firebase-Hosting-FFCA28?logo=firebase\u0026logoColor=black)](https://malzi.me)\n![Node.js](https://img.shields.io/badge/Node.js-24-339933?logo=node.js\u0026logoColor=white)\n[![CI](https://github.com/malziland/malzime/actions/workflows/ci.yml/badge.svg)](https://github.com/malziland/malzime/actions/workflows/ci.yml)\n[![Lighthouse Performance](https://img.shields.io/badge/Performance-100-brightgreen?logo=lighthouse)](https://github.com/malziland/malzime/actions/workflows/ci.yml)\n[![Lighthouse Accessibility](https://img.shields.io/badge/Accessibility-100-brightgreen?logo=lighthouse)](https://github.com/malziland/malzime/actions/workflows/ci.yml)\n[![Lighthouse Best Practices](https://img.shields.io/badge/Best_Practices-100-brightgreen?logo=lighthouse)](https://github.com/malziland/malzime/actions/workflows/ci.yml)\n[![Lighthouse SEO](https://img.shields.io/badge/SEO-100-brightgreen?logo=lighthouse)](https://github.com/malziland/malzime/actions/workflows/ci.yml)\n\n\u003e **[malzi.me](https://malzi.me)** — Jetzt ausprobieren\n\nWorkshop-Tool fuer Medienkompetenz und Datenschutz-Sensibilisierung. Zeigt Teilnehmer:innen, was KI-Algorithmen aus einem einzigen Foto ableiten koennten — inklusive Persoenlichkeitsprofil, Werbe-Targeting und Manipulationstrigger.\n\n**Alles erfunden. Nichts davon ist wahr oder bewiesen.**\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/01-startseite.png\" alt=\"malziME Startseite\" width=\"720\" /\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/02-mobile.png\" alt=\"malziME Mobile\" width=\"280\" /\u003e\n\u003c/p\u003e\n\n## Features\n\n- **Zwei Modi**: Serioese Analyse (sachlich) und Beast Mode (uebertrieben-provokant)\n- **Datenwert-Rechner**: Zeigt was ein Profil fuer Datenbroker wert ist\n- **Privacy-Check**: Erkennt ungewollt preisgegebene Informationen (Telefonnummern, Adressen, Kennzeichen)\n- **EXIF-Analyse**: Zeigt versteckte Kamera-Metadaten (client-seitig extrahiert)\n- **GPS-Karte**: Zeigt den Aufnahmeort auf einer Karte (GPS verlässt nie den Browser)\n- **Easter Egg**: Tierfotos bekommen ein lustiges Spass-Profil\n- **PDF-Export**: Ergebnisse als PDF speichern (fuer Workshop-Diskussionen)\n- **Demo-Fotos**: 3 anklickbare Stock-Fotos mit Fake-EXIF fuer Workshops (echte KI-Analyse, kein vorgefertigtes Ergebnis)\n- **Mehrsprachig vorbereitet**: i18n-System fuer UI, Prompts und Tierprofile (aktuell Deutsch)\n- **Wartungsmodus**: Admin-gesteuerter Wartungsmodus mit rotem Warn-Modal (blockiert Seite komplett)\n- **Kein Tracking**: Keine Cookies, keine Analytics, keine Werbung, keine Speicherung\n\n## Architektur\n\n```\npublic/                     Firebase Hosting (SPA, kein Build-Schritt)\n  index.html                Hauptseite\n  app.js                    Entry Point (ES Module)\n  js/                       Frontend-Module (api, dom, demo, exif, geocoding, i18n, render, state, stats, ui)\n  locales/                  Frontend-Locale-Dateien (de.json, manifest.json)\n  styles.css                Dark-Theme CSS + Print Styles\n  __tests__/                Vitest Frontend-Tests\n  impressum.html            Impressum\n  datenschutz.html          Datenschutzerklaerung\n  stats.html                Oeffentliche Nutzungsstatistik\n  fonts/                    Self-hosted: Inter + JetBrains Mono (woff2)\n  lib/leaflet/              Self-hosted: Leaflet 1.9.4\n  lib/exifr/                Self-hosted: exifr lite (EXIF-Parsing im Browser)\n\nfunctions/src/              Firebase Cloud Functions (2nd Gen, Node 24, europe-west1)\n  index.js                  HTTP-Handler (analyze + stats + admin Endpunkte)\n  config.js                 Konstanten, Modell-Listen, Limits\n  animal.js                 Personen-/Tier-Erkennung (Word-Boundary-Matching) + Easter-Egg-Profile\n  counter.js                Firestore-Zaehler: Stundenlimit, Totals, Stats, Boost, Reset\n  auth.js                   HMAC-basierte Admin-Token + Nonces\n  domains.js                Zentrale CORS-/Origin-Whitelist\n  notify.js                 ntfy Push-Benachrichtigungen bei Limit-Erreichung\n  middleware.js              Rate Limiting (IP-basiert, 200/10min), IP-Extraktion\n  upload.js                 Multipart- und JSON-Body-Parsing\n  vision.js                 Google Cloud Vision API (EU-Endpoint, TEXT + LABEL_DETECTION)\n  privacy.js                Datenschutz-Risiko-Erkennung aus OCR/Labels\n  gemini.js                 Vertex AI Gemini (Bildbeschreibung + Profilgenerierung)\n  i18n.js                   Backend-Locale-Loader (loadPrompts, loadAnimals, resolveLanguage)\n  locales/                  Backend-Locale-Dateien (de/prompts.js, de/animals.js, manifest.json)\n  __tests__/                Jest Unit-Tests\n```\n\n## Privacy-Architektur\n\nDatenschutz ist kein Feature — es ist das Fundament:\n\n- **EXIF-Extraktion im Browser**: exifr parsed die Metadaten lokal, GPS verlässt nie den Client\n- **Server bekommt kein GPS**: Nur komprimiertes Bild + Kamera-Hersteller/Modell (ohne GPS, ohne dateTimeOriginal)\n- **Geocoding direkt vom Browser**: Nominatim wird client-seitig aufgerufen, nicht ueber den Server\n- **Keine Speicherung**: Weder Bilder noch Profile werden gespeichert — alles RAM-only\n- **Keine externen Scripts**: Alle Assets self-hosted (Fonts, Leaflet, exifr). Kein Google Fonts CDN, kein unpkg, kein reCAPTCHA, kein Firebase SDK\n- **Bot-Schutz ohne Tracking**: Rate Limiting (IP), Honeypot-Feld, Timing-Check\n- **Strenge CSP**: Nur `self` + OpenStreetMap Tiles + Nominatim\n\n## Schnellstart\n\n```bash\n# 1. Repo klonen\ngit clone https://github.com/malziland/malzime.git\ncd malzime\n\n# 2. Firebase CLI installieren (falls noch nicht vorhanden)\nnpm i -g firebase-tools\nfirebase login\n\n# 3. Dependencies installieren\nnpm install                          # Frontend-Tests (Vitest)\ncd functions \u0026\u0026 npm install \u0026\u0026 cd .. # Backend\n\n# 4. Lokal testen\nfirebase emulators:start --only functions,hosting\n\n# 5. Deploy\nfirebase deploy --only functions,hosting\n```\n\nDetaillierte Anleitung: [`docs/SETUP.md`](docs/SETUP.md) | Eigene Instanz aufsetzen: [`docs/SELF-HOSTING.md`](docs/SELF-HOSTING.md)\n\n## API\n\n`POST /analyze` — JSON oder multipart/form-data\n\n### Request (JSON)\n\n```json\n{\n  \"imageBase64\": \"...\",\n  \"mimeType\": \"image/jpeg\",\n  \"filename\": \"upload.jpg\",\n  \"exif\": { \"make\": \"Apple\", \"model\": \"iPhone 15 Pro\" },\n  \"lang\": \"de\",\n}\n```\n\n| Feld | Typ | Beschreibung |\n|------|-----|--------------|\n| `imageBase64` | string | Base64-kodiertes Bild (client-seitig komprimiert) |\n| `mimeType` | string | `image/jpeg`, `image/png`, `image/webp`, `image/gif` |\n| `exif` | object | Kamera-Metadaten vom Client (ohne GPS!) |\n| `lang` | string | Sprachcode (`de`, `en`, ...). Default: `de` |\n\n### Response\n\n```json\n{\n  \"profiles\": {\n    \"normal\": {\n      \"categories\": {},\n      \"ad_targeting\": [],\n      \"manipulation_triggers\": [],\n      \"profileText\": \"\"\n    },\n    \"boost\": { \"...\" }\n  },\n  \"privacyRisks\": [],\n  \"exif\": {},\n  \"meta\": {\n    \"requestId\": \"abc12345\",\n    \"mode\": \"multimodal\"\n  }\n}\n```\n\n`mode` kann sein: `multimodal`, `animal`, `blocked`\n\nBei Tieren enthalten `profiles.normal` und `profiles.boost` ein lustiges Easter-Egg-Profil.\nBei blockierten Bildern ist `profiles: null` und `blockedReason` enthaelt den Grund.\n\n## Sicherheit\n\n- **Content Security Policy** mit strikter Whitelist\n- **HSTS** mit Preload\n- **X-Frame-Options: DENY**\n- **X-Content-Type-Options: nosniff**\n- **Magic-Byte-Validierung**: Server prueft JPEG/PNG/WebP/GIF-Header\n- **Honeypot-Feld** gegen Bots\n- **Rate Limiting**: 200 Requests / 10 Minuten pro IP\n- **Timing-Check**: Requests innerhalb von 2s nach Seitenaufruf werden verzoegert\n- **Prompt-Injection-Schutz**: User-Daten in XML-Tags isoliert + escapeXml() auf dynamische Inhalte\n- **HMAC-Admin-Tokens**: Kurzlebige signierte Tokens (30 Min) + Nonces (5 Min) fuer Admin-Aktionen\n- **Stundenlimit**: Rollendes 60-Minuten-Fenster (500 Analysen/Stunde, anonyme Timestamps in Firestore)\n- **Keine Datenspeicherung**: Bilder und Profile nur im RAM, kein Logging von Bilddaten\n\n## Tests\n\n```bash\n# Backend (Jest, 266 Tests)\ncd functions \u0026\u0026 npm test\n\n# Frontend (Vitest + jsdom, 139 Tests)\nnpm run test:frontend\n\n# E2E (Playwright, 2 Tests)\nnpm run test:e2e\n\n# Coverage\ncd functions \u0026\u0026 npm run test:coverage\nnpm run test:frontend:coverage\n\n# Linting\ncd functions \u0026\u0026 npm run lint           # Backend ESLint\nnpm run lint:frontend                  # Frontend ESLint\ncd functions \u0026\u0026 npm run format:check   # Backend Prettier\nnpm run format:frontend:check          # Frontend Prettier\n```\n\n**Backend (266 Tests):** HTTP-Handler, Admin-Endpunkte, Stats-Handler, HMAC-Auth, Nonce-Flow, Tier-Erkennung, Config, Counter, Middleware (Rate Limiting), Privacy-Risiken, Upload-Parsing, Vision API, Magic-Byte-Validierung, XML-Escaping, ntfy-Benachrichtigungen, i18n-Guardian, Gemini-Integration.\n\n**Frontend (139 Tests):** DOM-Helpers, State, Scan-Animation, Disclaimer-Modal, Limit-Banner, Maintenance-Modal, Geocoding, Render-Pipeline, API-Integration, Stats-Seite, i18n-Modul, i18n-Guardian.\n\n**E2E (2 Tests):** Playwright Smoke-Tests — Demo-Flow und fehlerfreies Laden.\n\n## CI/CD\n\nGitHub Actions Workflow `.github/workflows/ci.yml`:\n\n- **Tests + Lint** bei jedem Push und Pull Request (Backend + Frontend)\n- **Secret-Scan** via gitleaks (prueft auf versehentlich committete API-Keys)\n- **Dependabot** prueft monatlich auf unsichere Dependencies (npm + GitHub Actions)\n- **npm audit** im Backend-Job (blockiert bei kritischen Schwachstellen)\n- Deploy erfolgt manuell per `npx firebase deploy`\n\n## Tech-Stack\n\n| Komponente | Technologie |\n|-----------|-------------|\n| Hosting | Firebase Hosting |\n| Backend | Firebase Cloud Functions (2nd Gen, Node 24) |\n| KI-Beschreibung | Vertex AI Gemini 2.5 Flash (Multimodal) |\n| KI-Profile | Vertex AI Gemini 2.5 Flash (Text) |\n| Bilderkennung | Google Cloud Vision API (EU-Endpoint) |\n| Karten | Leaflet + OpenStreetMap (self-hosted) |\n| Geocoding | Nominatim (client-seitig) |\n| EXIF-Parsing | exifr (client-seitig im Browser) |\n| Fonts | Inter + JetBrains Mono (self-hosted, woff2) |\n| i18n | Eigenes Micro-Modul (Frontend JSON + Backend CommonJS Locales) |\n| Frontend | Vanilla JS, kein Framework, kein Build-Schritt |\n\n## Einschraenkungen\n\n- **EU Vision API** (`eu-vision.googleapis.com`) unterstuetzt nur `TEXT_DETECTION` und `LABEL_DETECTION`. `FACE_DETECTION` und `OBJECT_LOCALIZATION` sind nicht verfuegbar und wuerden den gesamten API-Call crashen.\n- **Safety-Filter**: Googles Sicherheitsfilter blockieren die Bildbeschreibung bei Fotos von Kindern oder Jugendlichen. In diesem Fall wird ein Fallback ueber Vision-API-Labels genutzt.\n- **Alters-Labels**: Vision API Labels wie \"Toddler\" oder \"Baby\" sind unzuverlaessig und werden gefiltert. Altersschaetzung erfolgt ausschliesslich durch Gemini anhand physischer Merkmale.\n- **Personen-Erkennung**: Die EU Vision API erkennt Personen in Outdoor-/Natur-Szenen oft nicht. Nur bei reinen Tier-Labels wird die Analyse blockiert — in allen anderen Faellen entscheidet Gemini.\n\n## Datenschutz\n\n- Keine Bilder, Profile oder Nutzerdaten werden gespeichert\n- Keine Tracking-Cookies, keine Analytics, keine Werbung\n- Kein Firebase SDK im Frontend, kein reCAPTCHA\n- KI-Analyse laeuft ueber Google Cloud (EU, europe-west1)\n- GPS-Daten verlassen nie den Browser des Nutzers\n- Details: [malzi.me/datenschutz](https://malzi.me/datenschutz)\n\n## Lizenz\n\nMIT — siehe [LICENSE](LICENSE)\n\n---\n\nErstellt von [malziland — digitale Wissensgestaltung](https://malziland.at)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalziland%2Fmalzime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmalziland%2Fmalzime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalziland%2Fmalzime/lists"}