{"id":48819219,"url":"https://github.com/or-carmeli/kubequest","last_synced_at":"2026-04-14T14:01:16.272Z","repository":{"id":341851094,"uuid":"1171704483","full_name":"or-carmeli/KubeQuest","owner":"or-carmeli","description":"Interactive Kubernetes learning game - quizzes, incident simulations, daily challenges, and a global leaderboard. React 19 + Supabase + Vercel.","archived":false,"fork":false,"pushed_at":"2026-04-11T20:32:43.000Z","size":46106,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-11T22:16:27.102Z","etag":null,"topics":["devops","k8s","kubernetes","learning","quiz","react","supabase","vite"],"latest_commit_sha":null,"homepage":"https://www.kubequest.online","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/or-carmeli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-03T14:18:33.000Z","updated_at":"2026-04-11T20:31:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/or-carmeli/KubeQuest","commit_stats":null,"previous_names":["or-carmeli/k8s-interactive-game","or-carmeli/kubequest"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/or-carmeli/KubeQuest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/or-carmeli%2FKubeQuest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/or-carmeli%2FKubeQuest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/or-carmeli%2FKubeQuest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/or-carmeli%2FKubeQuest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/or-carmeli","download_url":"https://codeload.github.com/or-carmeli/KubeQuest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/or-carmeli%2FKubeQuest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31799411,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T11:13:53.975Z","status":"ssl_error","status_checked_at":"2026-04-14T11:13:53.299Z","response_time":153,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["devops","k8s","kubernetes","learning","quiz","react","supabase","vite"],"created_at":"2026-04-14T14:01:12.760Z","updated_at":"2026-04-14T14:01:16.245Z","avatar_url":"https://github.com/or-carmeli.png","language":"JavaScript","readme":"# ☸️ KubeQuest\n\n**Interactive Kubernetes learning game for DevOps/SRE engineers.**\n\nPractice real-world Kubernetes scenarios, sharpen your troubleshooting skills, and build production-level knowledge - through interactive quizzes, incident simulations, and daily challenges.\n\n[![Live Demo](https://img.shields.io/badge/Live%20Demo-kubequest.online-00D4FF?style=flat-square\u0026logo=vercel)](https://www.kubequest.online/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green?style=flat-square)](LICENSE)\n[![CI](https://img.shields.io/github/actions/workflow/status/or-carmeli/KubeQuest/ci.yml?branch=main\u0026style=flat-square\u0026label=CI)](https://github.com/or-carmeli/KubeQuest/actions/workflows/ci.yml)\n[![Security](https://img.shields.io/github/actions/workflow/status/or-carmeli/KubeQuest/security.yml?branch=main\u0026style=flat-square\u0026label=security)](https://github.com/or-carmeli/KubeQuest/actions/workflows/security.yml)\n[![React](https://img.shields.io/badge/React-19-61DAFB?style=flat-square\u0026logo=react)](https://react.dev)\n[![Vite](https://img.shields.io/badge/Vite-5-646CFF?style=flat-square\u0026logo=vite)](https://vitejs.dev)\n[![Supabase](https://img.shields.io/badge/Supabase-PostgreSQL-3ECF8E?style=flat-square\u0026logo=supabase)](https://supabase.com)\n[![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square\u0026logo=docker)](https://github.com/or-carmeli/KubeQuest/pkgs/container/kubequest)\n[![Status](https://img.shields.io/badge/Status-status.kubequest.online-10B981?style=flat-square\u0026logo=statuspage)](https://status.kubequest.online)\n\n---\n\n[kubequest.online](https://www.kubequest.online/) - no registration required, works instantly in guest mode.\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"public/app-demo-gif-english.gif\" alt=\"KubeQuest Demo\" width=\"420\"\u003e\n\u003c/div\u003e\n\n---\n\n## Table of Contents\n\n- [Features](#features)\n- [War Room](#war-room---kubernetes-incident-sandbox)\n- [Learning Path](#learning-path)\n- [Tech Stack](#tech-stack)\n- [Architecture](#architecture)\n- [Security Model](#security-model)\n- [Authentication Flow](#authentication-flow)\n- [Observability](#observability)\n- [CI/CD \u0026 Supply Chain Security](#cicd--supply-chain-security)\n- [Kubernetes Deployment](#kubernetes-deployment)\n- [Local Development](#local-development)\n- [Docker](#docker)\n- [Testing](#testing)\n- [Project Structure](#project-structure)\n- [Contributing](#contributing)\n- [Performance Insights](#performance-insights-dev-only-observability)\n- [Changelog](#changelog)\n- [Disclaimer](#disclaimer)\n- [License](#license)\n\n---\n\n## Features\n\n- **🧠 Topic Quizzes** - 8 topics x 3 difficulty levels, progressively unlocked\n- **🔥 Daily Challenge** - 5 fresh questions every day\n- **🎲 Mixed Quiz** - random questions across all topics\n- **🎯 Interview Mode** - mandatory timer, hints disabled, exam pressure\n- **📖 Kubernetes Guide** - built-in cheatsheet for quick lookup while practicing\n- **🗺️ Roadmap View** - visual learning path through all topics and levels\n- **📉 Weak Area Card** - surfaces your lowest-accuracy topic automatically\n- **↩️ Quiz Resume** - continue where you left off after refresh or navigation\n- **🏆 Leaderboard** - global top scores\n- **🏅 Achievements** - milestone-based reward system\n- **🌐 Hebrew / English** - full bilingual support with RTL layout\n- **👤 Guest Mode** - no account needed; sign up to sync progress across devices\n- **📊 Real-Time Monitoring** - live status page with health checks, uptime history, and auto-detected incidents\n- **🚨 War Room** - interactive Kubernetes incident simulator with realistic troubleshooting scenarios, signal-based investigation, and scoring\n\n### War Room - Kubernetes Incident Sandbox\n\nWar Room is an interactive incident sandbox that presents realistic troubleshooting scenarios inspired by production Kubernetes failures. Users investigate system signals using simulated kubectl commands, discover clues step by step, and identify the root cause of failures such as misconfigured Services, missing ConfigMaps, broken probes, storage issues, and DNS failures. Each incident is scored based on investigation quality, efficiency, and accuracy. Solved incidents contribute to the user's overall leaderboard score.\n\n---\n\n## Learning Path\n\nThe learning path is structured around core Kubernetes knowledge areas:\n\n| # | Topic | Key Areas |\n|---|-------|-----------|\n| 1 | **Workloads \u0026 Scheduling** | Pods, Deployments, StatefulSets, DaemonSets, Jobs, taints/tolerations, HPA |\n| 2 | **Networking \u0026 Services** | Services, Ingress, CoreDNS, NetworkPolicy, kube-proxy |\n| 3 | **Cluster Operations** | kubeadm init/join/upgrade, etcd backup \u0026 restore, Static Pods, certificates, control plane troubleshooting |\n| 4 | **Config \u0026 Secrets** | ConfigMaps, Secrets, RBAC, ServiceAccounts, Pod Security Admission |\n| 5 | **Storage \u0026 Helm** | PV/PVC, StorageClass, dynamic provisioning, access modes, Helm |\n| 6 | **Troubleshooting \u0026 Debugging** | CrashLoopBackOff, ImagePullBackOff, Node NotReady, DNS issues, probe failures |\n| 7 | **OS \u0026 Linux Deep Dive** | Processes, memory, CPU, networking, container runtime internals |\n| 8 | **Argo \u0026 GitOps** | ArgoCD, sync policies, ApplicationSets, App of Apps *(Coming Soon)* |\n\n---\n\n## Tech Stack\n\n| Layer | Technology |\n|-------|-----------|\n| Frontend | [React 19](https://react.dev) + [Vite 5](https://vitejs.dev) |\n| Backend | [Supabase](https://supabase.com) (PostgreSQL + Auth + Edge Functions) |\n| Hosting | [Vercel](https://vercel.com) (Edge Network + CDN) |\n| Containerization | Docker (multi-stage build, nginx:alpine, ~25MB) |\n| Orchestration | Kubernetes (Deployment, HPA, Ingress, cert-manager) |\n| CI/CD | GitHub Actions (build, scan, sign, attest) |\n| Supply Chain | [Cosign](https://docs.sigstore.dev/cosign/overview/) + [Trivy](https://trivy.dev/) + SBOM + Provenance |\n| Security | CSP, HSTS, CORS, RLS, CodeQL, npm audit, Gitleaks, Kyverno |\n| Monitoring | Supabase Edge Functions + pg_cron (60s interval) |\n| Error Tracking | [Sentry](https://sentry.io) (production-only, free tier) |\n| Testing | [Vitest](https://vitest.dev) |\n| Release Management | [Release Please](https://github.com/googleapis/release-please) (automated versioning + CHANGELOG) |\n| Dependency Management | [Dependabot](https://docs.github.com/en/code-security/dependabot) (weekly - npm, Docker, Actions) |\n\n---\n\n## Architecture\n\n### Runtime\n\n```mermaid\nflowchart TB\n    USER([User])\n\n    subgraph Frontend[\"Frontend\"]\n        SPA[\"React SPA (Vercel)\"]\n        PWA[\"PWA Service Worker\u003cbr/\u003eOffline Cache\"]\n    end\n\n    subgraph Backend[\"Supabase Backend\"]\n        AUTH[\"Authentication\"]\n        API[\"API / Data Access\"]\n        EDGE[\"Edge Functions\u003cbr/\u003eHealth Checks\"]\n    end\n\n    subgraph Database[\"Database\"]\n        DB[(\"PostgreSQL\")]\n    end\n\n    USER --\u003e|HTTPS| SPA\n    SPA --\u003e PWA\n    SPA --\u003e AUTH\n    SPA --\u003e API\n    API --\u003e DB\n    EDGE --\u003e AUTH\n    EDGE --\u003e API\n\n    style Frontend fill:#111827,stroke:#00D4FF,stroke-width:2px,color:#ffffff\n    style Backend fill:#111827,stroke:#A855F7,stroke-width:2px,color:#ffffff\n    style Database fill:#111827,stroke:#F59E0B,stroke-width:2px,color:#ffffff\n```\n\n### App Flow\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant SPA as React SPA\n    participant API as Supabase (Auth + RPC + DB)\n\n    User-\u003e\u003eSPA: Open app\n    SPA-\u003e\u003eAPI: Auth check (PKCE)\n    API--\u003e\u003eSPA: Session or guest mode\n\n    User-\u003e\u003eSPA: Start quiz\n    SPA-\u003e\u003eAPI: Fetch questions (no answers shipped)\n    User-\u003e\u003eSPA: Submit answer\n    SPA-\u003e\u003eAPI: Server validates + scores\n    API--\u003e\u003eSPA: Result + explanation\n\n    SPA-\u003e\u003eAPI: Save progress\n    SPA--\u003e\u003eUser: Results + achievements\n```\n\nThe client never sees correct answers until after submission — all scoring is server-authoritative via `SECURITY DEFINER` RPC functions.\n\n### Stack Layers\n\n**Frontend** - React single-page application built with Vite, deployed on Vercel. Includes a manual service worker for offline caching and a PWA manifest for installability. All routing is handled client-side.\n\n**Platform** - Vercel Edge Network serves static assets and runs Edge Middleware for request validation, host header verification, and automated scanner blocking. Security headers (CSP, HSTS, COOP, CORP) are enforced via `vercel.json`.\n\n**Backend** - Supabase provides authentication, real-time subscriptions, and a PostgreSQL database. All sensitive operations (answer validation, score updates) run through `SECURITY DEFINER` RPC functions that enforce server-side logic. A Supabase Edge Function runs periodic health checks across all services.\n\n---\n\n## Security Model\n\n```mermaid\nflowchart LR\n    USER([User]) --\u003e|HTTPS| EDGE[\"Edge\u003cbr/\u003eHSTS · CORS\"]\n    EDGE --\u003e APP[\"App\u003cbr/\u003eCSP · No Inline\"]\n    APP --\u003e API[\"API\u003cbr/\u003eRPC · Rate Limit\"]\n    API --\u003e DB[(\"DB\u003cbr/\u003eRLS\")]\n\n    style EDGE fill:#111827,stroke:#EF4444,stroke-width:2px,color:#fff\n    style APP fill:#111827,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style API fill:#111827,stroke:#A855F7,stroke-width:2px,color:#fff\n    style DB fill:#111827,stroke:#F59E0B,stroke-width:2px,color:#fff\n```\n\nSix layers of defense — no layer trusts the one above it:\n\n| Layer | Controls |\n|-------|----------|\n| Edge | HTTPS enforced, HSTS (1 year, preload), strict CORS |\n| Application | Content Security Policy (no inline scripts), X-Frame-Options DENY, COOP/CORP same-origin |\n| API | `SECURITY DEFINER` RPC endpoints, rate limiting on answer verification |\n| Database | Row Level Security on all tables, server-side validation |\n| Container | Cosign-signed images, SBOM attestations, Trivy scanning, Kyverno policy enforcement |\n| Code | CodeQL static analysis, npm audit, Gitleaks secret scanning, Dependabot weekly updates |\n\n---\n\n## Authentication Flow\n\nKubeQuest uses Supabase Authentication for user management. Authenticated users have their progress synced to the cloud, while guest mode allows full access without registration - progress is stored locally. Password reset uses the PKCE flow for secure email-based recovery. Sessions persist across page reloads via Supabase's session storage.\n\n```mermaid\nflowchart LR\n    START[\"App Start\"] --\u003e CHECK{Session?}\n    CHECK --\u003e|Yes| LOAD[\"Load User\"] --\u003e AUTHED[\"Authenticated\"]\n    CHECK --\u003e|No| GUEST{Guest?}\n    GUEST --\u003e|Yes| GMODE[\"Guest Mode\"]\n    GUEST --\u003e|No| LOGIN[\"Login / Signup\"] --\u003e AUTH[\"Supabase Auth\"] --\u003e LOAD\n    RESET[\"Password Reset\u003cbr/\u003ePKCE\"] -.-\u003e LOGIN\n\n    style CHECK fill:#111827,stroke:#A855F7,stroke-width:2px,color:#fff\n    style GUEST fill:#111827,stroke:#A855F7,stroke-width:2px,color:#fff\n    style START fill:#111827,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style LOAD fill:#111827,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style LOGIN fill:#111827,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style GMODE fill:#111827,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style AUTH fill:#111827,stroke:#10B981,stroke-width:2px,color:#fff\n    style AUTHED fill:#111827,stroke:#10B981,stroke-width:2px,color:#fff\n    style RESET fill:#111827,stroke:#EF4444,stroke-dasharray:5 5,color:#fff\n```\n\n---\n\n## Observability\n\n```mermaid\nflowchart TD\n    CRON[\"pg_cron · every 60s\"] --\u003e EDGE[\"Edge Function\"]\n    EDGE --\u003e DB[\"DB\"] \u0026 API[\"API\"] \u0026 QUIZ[\"Quiz\"] \u0026 LB[\"Leaderboard\"] \u0026 AUTH[\"Auth\"]\n    EDGE --\u003e|results| STORE[(\"Status Tables\")]\n    STORE --\u003e|polls 30s| UI[\"Status Page\"]\n    UI -.-\u003e USER([User])\n    EDGE -.-\u003e|incident| RESEND[\"Resend API\"]\n    RESEND -.-\u003e|email alert| USER\n\n    style CRON fill:#111827,stroke:#A855F7,stroke-width:2px,color:#fff\n    style EDGE fill:#111827,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style STORE fill:#111827,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style UI fill:#111827,stroke:#10B981,stroke-width:2px,color:#fff\n    style RESEND fill:#111827,stroke:#e53e3e,stroke-width:2px,color:#fff\n    style USER fill:#111827,stroke:#9CA3AF,stroke-width:2px,color:#fff\n```\n\nThe core of the observability stack is a **self-monitoring loop** built entirely on Supabase:\n\n1. **pg_cron** triggers a Supabase Edge Function every 60 seconds\n2. The Edge Function health-checks all 5 services (DB, API, Quiz Engine, Leaderboard, Auth)\n3. Results are written to PostgreSQL status tables (append-only for uptime history)\n4. 3 consecutive failures auto-create an incident and send an email alert via Resend\n5. The frontend polls these tables every 30 seconds and renders a live status page\n\nAdditional layers: **Sentry** captures client-side errors and Web Vitals, **GitHub Actions** run external uptime checks (every 30min) and synthetic smoke tests (every 6h).\n\n### Health Checks\n\n| Service | Check |\n|---------|-------|\n| Database | `SELECT` on `user_stats` |\n| Content API | `get_mixed_questions` RPC |\n| Quiz Engine | `check_quiz_answer` RPC |\n| Leaderboard | `get_leaderboard` RPC |\n| Authentication | GoTrue `/auth/v1/health` |\n\n- **Status classification** — operational (\u003c2s), degraded (\u003e2s), down (error)\n- **Auto-incident detection** — 3 consecutive failures trigger automatic incident creation + email alert via Resend\n- **Data retention** — append-only `system_status_history` table for uptime tracking\n\nFull documentation: [docs/monitoring.md](docs/monitoring.md) | Live status: [status.kubequest.online](https://status.kubequest.online)\n\n### Error Tracking (Sentry)\n\nClient-side errors are reported to [Sentry](https://sentry.io) in production only. The integration is error-monitoring only — no performance tracing, session replay, or profiling (free-tier friendly).\n\n**What is captured:**\n- Uncaught exceptions and unhandled promise rejections\n- React render crashes (via ErrorBoundary)\n- Failures in data load/save, quiz answer submission, and question fetching\n\n**What is NOT sent:**\n- Auth tokens, JWTs, or Supabase keys\n- Email addresses or personally identifying information\n- SQL queries, table names, or raw backend error payloads\n\n**Configuration:** Set `VITE_SENTRY_DSN` as an environment variable (Vercel / `.env`). Without it, Sentry is disabled and the app runs normally. For source map uploads in CI, also set `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, and `SENTRY_PROJECT` as GitHub Actions secrets.\n\n### Web Vitals\n\nCore Web Vitals (LCP, CLS, INP) are reported to Sentry as breadcrumbs and tags, correlating performance with errors. Also tracked independently by Vercel Speed Insights.\n\n### Synthetic Monitoring\n\nA GitHub Actions workflow (`synthetic-monitor.yml`) runs every 6 hours and verifies:\n- Homepage returns 200 with critical assets\n- Supabase API is reachable\n- Response time is under 3 seconds\n- Security headers are present\n\n### Uptime Monitoring\n\nExternal uptime checks (`uptime.yml`) ping the homepage and Supabase auth every 30 minutes. Failed runs indicate downtime.\n\nFull documentation: [docs/observability.md](docs/observability.md)\n\n---\n\n## CI/CD \u0026 Supply Chain Security\n\n### Workflows\n\n| Workflow | Trigger | Purpose |\n|----------|---------|---------|\n| [ci.yml](.github/workflows/ci.yml) | Every PR / push to `main`, `dev` | Build + npm audit + Gitleaks + CodeQL + Trivy + K8s policy (PR gate) |\n| [docker.yml](.github/workflows/docker.yml) | Push to `main` / version tags | Build, scan, push to GHCR, SBOM + provenance, Cosign sign |\n| [release-please.yml](.github/workflows/release-please.yml) | Push to `main` | Auto-bump version, generate changelog, create GitHub Release |\n| [security.yml](.github/workflows/security.yml) | Weekly (Monday) + on-demand | npm audit + Trivy + CodeQL for newly disclosed CVEs |\n\n### PR Gate (shift-left)\n\nAll six CI jobs must pass before a PR can merge:\n\n```mermaid\nflowchart LR\n    PR[\"Pull Request\"] --\u003e BUILD[\"Build\"]\n    PR --\u003e AUDIT[\"npm Audit\"]\n    PR --\u003e GITLEAKS[\"Gitleaks\"]\n    PR --\u003e CODEQL[\"CodeQL\"]\n    PR --\u003e TRIVY[\"Trivy Scan\"]\n    PR --\u003e K8S[\"K8s Policies\"]\n    BUILD --\u003e MERGE[\"Merge\"]\n    AUDIT --\u003e MERGE\n    GITLEAKS --\u003e MERGE\n    CODEQL --\u003e MERGE\n    TRIVY --\u003e MERGE\n    K8S --\u003e MERGE\n\n    style PR fill:#1a1a2e,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style BUILD fill:#1a1a2e,stroke:#A855F7,stroke-width:2px,color:#fff\n    style AUDIT fill:#1a1a2e,stroke:#EF4444,stroke-width:2px,color:#fff\n    style GITLEAKS fill:#1a1a2e,stroke:#EF4444,stroke-width:2px,color:#fff\n    style CODEQL fill:#1a1a2e,stroke:#EF4444,stroke-width:2px,color:#fff\n    style TRIVY fill:#1a1a2e,stroke:#EF4444,stroke-width:2px,color:#fff\n    style K8S fill:#1a1a2e,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style MERGE fill:#1a1a2e,stroke:#10B981,stroke-width:2px,color:#fff\n```\n\n### Publish Pipeline (post-merge)\n\n```mermaid\nflowchart TD\n    BOT[\"Dependabot\"] -.-\u003e|weekly PRs| PUSH\n    PUSH[\"Push to main\"] --\u003e RP[\"Release Please\u003cbr/\u003eversion bump + CHANGELOG\"]\n    RP --\u003e|release PR merged| TAG[\"Git Tag v*.*.*\"]\n    TAG --\u003e SCAN[\"Build \u0026 Trivy Scan\"] --\u003e GHCR[\"Push to GHCR\"] --\u003e ATTEST[\"SBOM + Provenance\"] --\u003e SIGN[\"Cosign Sign\"] --\u003e VERIFY[\"Verify\"]\n\n    style PUSH fill:#1a1a2e,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style RP fill:#1a1a2e,stroke:#A855F7,stroke-width:2px,color:#fff\n    style TAG fill:#1a1a2e,stroke:#A855F7,stroke-width:2px,color:#fff\n    style SCAN fill:#1a1a2e,stroke:#EF4444,stroke-width:2px,color:#fff\n    style GHCR fill:#1a1a2e,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style ATTEST fill:#1a1a2e,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style SIGN fill:#1a1a2e,stroke:#10B981,stroke-width:2px,color:#fff\n    style VERIFY fill:#1a1a2e,stroke:#10B981,stroke-width:2px,color:#fff\n    style BOT fill:#1a1a2e,stroke:#00D4FF,stroke-dasharray:5 5,color:#fff\n```\n\n**Dependabot** runs weekly and opens PRs automatically for npm packages, the Dockerfile base image, and GitHub Actions - keeping dependencies up to date and patching known vulnerabilities.\n\n\u003e **Production** runs on Vercel + Supabase. The `k8s/` manifests and Docker image on GHCR enable self-hosting on any Kubernetes cluster.\n\n### Image Tags\n\n| Trigger | Tag | Example |\n|---------|-----|---------|\n| Push to `main` | `latest` + `sha-\u003ccommit\u003e` + `package.json` version | `latest`, `sha-a1b2c3d`, `2.6.0` |\n| Release Please tag `v2.7.0` | Semver + `sha-\u003ccommit\u003e` | `2.7.0`, `sha-a1b2c3d` |\n| Manual dispatch | `sha-\u003ccommit\u003e` | `sha-a1b2c3d` |\n\n### Supply Chain Security\n\n- **Secret scanning** - [Gitleaks](https://gitleaks.io/) scans every PR for leaked credentials, API keys, and tokens\n- **Vulnerability scanning** - [Trivy](https://trivy.dev/) scans the image before push; the workflow fails on HIGH and CRITICAL vulnerabilities (unfixed CVEs excluded)\n- **K8s policy enforcement** - [Kyverno](https://kyverno.io/) CLI validates manifests in CI; runtime admission policies available for cluster-side enforcement ([docs](docs/k8s-admission-policies.md))\n- **SBOM** - Software Bill of Materials attached to every published image\n- **Provenance** - build provenance attestation (`mode=max`) provides cryptographic proof of build origin\n- **Keyless signing** - [Cosign](https://docs.sigstore.dev/cosign/overview/) signs images by digest using GitHub OIDC; no secret keys to manage or rotate\n- **In-pipeline verification** - the signature is verified in CI before the workflow completes\n\n### Verify Locally\n\n```bash\ncosign verify \\\n  --certificate-oidc-issuer https://token.actions.githubusercontent.com \\\n  --certificate-identity-regexp \"github\\.com/or-carmeli/KubeQuest\" \\\n  ghcr.io/or-carmeli/kubequest:latest\n```\n\n### Deploying by Digest\n\nEvery workflow run outputs an immutable image reference by digest. Use it in Kubernetes manifests, Helm values, or ArgoCD application specs to pin the exact image that was built, scanned, and signed:\n\n```yaml\nimage: ghcr.io/or-carmeli/kubequest@sha256:\u003cdigest\u003e\n```\n\n---\n\n## Kubernetes Deployment\n\nThe `k8s/` directory contains production-ready manifests to deploy KubeQuest on any Kubernetes cluster.\n\n```mermaid\nflowchart TB\n    INTERNET([Internet]) --\u003e|HTTPS| ING[\"Ingress\u003cbr/\u003enginx + TLS via cert-manager\"]\n    ING --\u003e SVC[\"Service\u003cbr/\u003eClusterIP :80\"]\n    SVC --\u003e POD1[\"Pod 1\"]\n    SVC --\u003e POD2[\"Pod 2\"]\n\n    HPA[\"HPA\u003cbr/\u003e2-10 replicas\u003cbr/\u003e70% CPU target\"] -.-\u003e|scales| SVC\n\n    subgraph NS[\"Namespace: kubequest\"]\n        ING\n        SVC\n        POD1\n        POD2\n        HPA\n    end\n\n    style NS fill:#111827,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style ING fill:#1a1a2e,stroke:#A855F7,stroke-width:2px,color:#fff\n    style SVC fill:#1a1a2e,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style HPA fill:#1a1a2e,stroke:#10B981,stroke-dasharray:5 5,color:#fff\n```\n\n| Manifest | What it does |\n|----------|-------------|\n| `namespace.yaml` | Isolated namespace `kubequest` |\n| `deployment.yaml` | 2 replicas, resource limits (200m CPU / 128Mi), liveness + readiness probes |\n| `service.yaml` | ClusterIP on port 80 |\n| `ingress.yaml` | nginx Ingress with TLS via cert-manager, HTTP-to-HTTPS redirect |\n| `hpa.yaml` | HorizontalPodAutoscaler: 2-10 pods at 70% CPU |\n\n```bash\nkubectl apply -f k8s/\n```\n\n\u003e Requires: nginx ingress controller + cert-manager installed in the cluster.\n\n---\n\n## Local Development\n\n### Prerequisites\n\n- Node.js 18+\n- A free [Supabase](https://supabase.com) account _(optional - guest mode works without it)_\n\n### Setup\n\n```bash\ngit clone https://github.com/or-carmeli/KubeQuest.git\ncd KubeQuest\nnpm install\ncp .env.example .env   # add your Supabase credentials\nnpm run dev            # → http://localhost:5173\n```\n\n### Environment Variables\n\n```env\nVITE_SUPABASE_URL=https://your-project-id.supabase.co\nVITE_SUPABASE_ANON_KEY=your_supabase_anon_key_here\nVITE_SENTRY_DSN=                # optional — enables error tracking in production\n```\n\n\u003e Auth, leaderboard, and cross-device sync require a Supabase project. All other features work without credentials. Sentry is optional — omit the DSN to disable error tracking.\n\n### Available Scripts\n\n```bash\nnpm run dev      # development server\nnpm run build    # production build\nnpm run preview  # preview production build locally\nnpm run test     # run tests (vitest)\n```\n\n### Supabase Setup\n\nCreate a `user_stats` table:\n\n| Column | Type |\n|--------|------|\n| `user_id` | `uuid` - unique, references `auth.users` |\n| `username` | `text` |\n| `total_answered` | `int4` |\n| `total_correct` | `int4` |\n| `total_score` | `int4` |\n| `max_streak` | `int4` |\n| `current_streak` | `int4` |\n| `completed_topics` | `jsonb` |\n| `achievements` | `jsonb` |\n| `topic_stats` | `jsonb` |\n| `updated_at` | `timestamptz` |\n\nEnable Row Level Security:\n\n```sql\ncreate policy \"Users can manage own stats\"\non public.user_stats\nfor all\nto public\nusing (auth.uid() = user_id);\n```\n\n---\n\n## Docker\n\nKubeQuest is a **Single Page Application (SPA)** - React handles all navigation client-side from a single `index.html` file. The web server must serve `index.html` for every URL so React can take over routing.\n\nThe Dockerfile uses a **multi-stage build** to keep the production image small and clean:\n\n```\nStage 1 - Builder  (node:20-alpine)\n  npm ci              → install dependencies\n  npm run build       → compile React source → static HTML/CSS/JS in /dist\n\nStage 2 - Runner   (nginx:alpine)\n  copies /dist        → only the built output (no Node.js, no source code)\n  serves via nginx    → fast, lightweight web server with SPA routing\n```\n\nFinal image size: ~25MB (vs ~500MB if Node.js were included).\n\n```bash\ndocker build -t kubequest .\ndocker run -p 8080:80 kubequest\n# → http://localhost:8080\n```\n\n---\n\n## Testing\n\n| Framework | Coverage |\n|-----------|----------|\n| [Vitest](https://vitest.dev) | Quiz persistence, level unlocking, state corruption recovery, i18n |\n\n```bash\nnpm run test\n```\n\nKey test areas:\n- Quiz state persistence and resume across page reloads\n- Level unlock progression (easy -\u003e medium -\u003e hard)\n- Cross-quiz-type isolation (daily, mixed, topic, bookmarks)\n- Corrupt localStorage detection and recovery (NaN prevention)\n- Language switching behavior\n- Backward compatibility with older saved states\n\n---\n\n## Project Structure\n\n```\nsrc/\n  App.jsx                  # Main application (UI + state)\n  main.jsx                 # Entry point (Sentry init, Web Vitals, React mount)\n  api/                     # Supabase RPCs (quiz, monitoring, analytics, telemetry)\n  content/                 # Quiz questions, daily challenges, incidents, scenarios\n  components/              # UI components (quiz, roadmap, stats, status, architecture)\n    architecture/          # Architecture scenario components\n    shared/                # Reusable UI primitives\n  hooks/                   # Custom React hooks\n  utils/                   # Helpers (storage, i18n, bidi, telemetry, persistence)\npublic/\n  sw.js                    # Service worker (offline cache, build-stamped)\nk8s/                       # Kubernetes manifests (namespace, deployment, service, ingress, HPA)\n  policies/                # Kyverno policies (trusted registries, no-latest, resource limits)\nsupabase/\n  migrations/              # Database schema and RPCs\n  functions/               # Edge Functions (health-check, collect-metrics)\n.github/\n  workflows/               # CI, Docker, security, synthetic monitoring, uptime, seed\n  dependabot.yml           # Weekly dependency updates (npm, Docker, Actions)\ndocs/                      # Monitoring, observability, K8s admission policies\n```\n\n---\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for the full release history.\n\n---\n\n## Contributing\n\nContributions are welcome - new questions, bug fixes, UI improvements.\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions and question format guidelines.\n\n---\n\n## Performance Insights (Dev-only Observability)\n\nA dev-only observability dashboard built on real client-side telemetry. No simulated or mock data. Available exclusively in development mode and fully tree-shaken from production builds.\n\nKey capabilities:\n\n- **Real-time Web Vitals** (LCP, INP, CLS) with session-vs-global comparison against Google CrUX p75 benchmarks\n- **Request latency** (P95) and **error rate** computed from instrumented `fetch` calls, with time-range filtering (30s to full session)\n- **Traffic awareness** (requests/sec) and **data confidence scoring** to distinguish meaningful signals from insufficient samples\n- **Health assessment** that combines latency, errors, vitals, and network failures into a single weighted status (Healthy / Degraded / Unhealthy / Idle)\n- **Insights engine** that derives analytical conclusions from metric correlations, not just threshold breaches\n- **Navigation timing**, client error tracking, and user flow metrics (route visits, quiz completion rates, session duration)\n\nAccess it via the sidebar menu in `npm run dev`. The feature is triple-gated: menu visibility, route registration, and component-level guard are all conditioned on `import.meta.env.DEV`.\n\n```mermaid\nflowchart LR\n    BROWSER[\"Browser Runtime\u003cbr/\u003eweb-vitals · fetch · errors\"] --\u003e COLLECT[\"Telemetry\u003cbr/\u003eCollector\"]\n    COLLECT --\u003e SNAPSHOT[\"Snapshot Builder\u003cbr/\u003eTime-range Filter\"]\n    SNAPSHOT --\u003e ANALYSIS[\"Analysis Engine\u003cbr/\u003eHealth · Insights\"]\n    ANALYSIS --\u003e UI[\"Dashboard\u003cbr/\u003eUI\"]\n\n    style BROWSER fill:#1a1a2e,stroke:#00D4FF,stroke-width:2px,color:#fff\n    style COLLECT fill:#1a1a2e,stroke:#A855F7,stroke-width:2px,color:#fff\n    style SNAPSHOT fill:#1a1a2e,stroke:#F59E0B,stroke-width:2px,color:#fff\n    style ANALYSIS fill:#1a1a2e,stroke:#10B981,stroke-width:2px,color:#fff\n    style UI fill:#1a1a2e,stroke:#00D4FF,stroke-width:2px,color:#fff\n```\n\n---\n\n## Disclaimer\n\nKubeQuest is an independent learning project and is not affiliated with, sponsored by, or endorsed by the Linux Foundation, the CNCF, or any certification body.\nCKA and Kubernetes are trademarks of the Cloud Native Computing Foundation.\n\n---\n\n## License\n\n[MIT](LICENSE) © 2026 Or Carmeli\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2For-carmeli%2Fkubequest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2For-carmeli%2Fkubequest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2For-carmeli%2Fkubequest/lists"}