Files
rootnode-academy/README.md
T
ced4568andClaude Sonnet 5 42dfb1c31b docs(phase2a): document gateway and student launch architecture
- docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md — the full Phase 2A record: node-pty
  root cause and fix, Gateway architecture, student flow (entry through
  reset/recreate), API routes added, HTTP/WebSocket routing design (and why
  the lab-session cookie exists), security model (what's checked, and the
  explicit statement that this is pilot identity, not production auth),
  reset/recreate and reaper-recovery semantics, and known limitations found
  during implementation (the identity instance's k3s resource cost and its
  network_mode: bridge requirement, the Gateway's in-memory cache scope, the
  ?sid= check's coverage). BUILT NOW vs. FUTURE (Phase 2B) called out
  explicitly per the governing requirement.
- docs/LAB_ARCHITECTURE.md, docs/ENGINEERING_SOP.md — status updated to
  reflect Phase 2A is implemented and tested, not just Phase 1; roadmap item 5
  updated accordingly; ENGINEERING_SOP's Provisioner section corrected (it's
  no longer un-fronted — the Gateway is the only caller now).
- README.md — Multi-Student Deployment section updated to describe the
  Gateway and student flow, not just the Provisioner; clarifies the
  node-pty fix is the only change to the Lab image itself.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-18 11:18:43 -05:00

209 lines
8.0 KiB
Markdown

<div align="center">
<img src="frontend/public/logo.svg" alt="KubeKosh" height="96" />
# KubeKosh — RootNode Academy Edition
**A browser-based Kubernetes lab for hands-on learners.**
Real K3s. Real `kubectl`. Instant validation. No cloud account, no local cluster.
[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](LICENSE)
[![Platforms](https://img.shields.io/badge/platforms-amd64%20%7C%20arm64-lightgrey?style=flat-square)](#)
[![Kubernetes](https://img.shields.io/badge/Kubernetes-K3s-326CE5?style=flat-square&logo=kubernetes&logoColor=white)](https://k3s.io)
[![Docker](https://img.shields.io/badge/Docker-required-2496ED?style=flat-square&logo=docker&logoColor=white)](https://docs.docker.com/get-docker/)
[![Maintained by ced4568](https://img.shields.io/badge/maintained%20by-ced4568-181717?style=flat-square&logo=github)](https://github.com/ced4568)
</div>
---
> **Attribution.** This is a branded, modified fork of the excellent
> [`zeborg/kubekosh`](https://github.com/zeborg/kubekosh), licensed under Apache 2.0.
> RootNode Academy adds custom scenarios, branding, and multi-student deployment tooling on
> top of the upstream project. Original copyright and license notices are preserved in
> [`LICENSE`](LICENSE). Modified files are marked per Apache 2.0 §4. Full credit to
> the original author for the core platform.
---
## What It Is
KubeKosh runs a real single-node [K3s](https://k3s.io) cluster inside one Docker
container and pairs it with a browser terminal and automated scenario validation.
A learner opens a tab, gets a live cluster, runs real `kubectl`, and clicks **Validate**
to check their work against actual cluster state.
In the RootNode Academy curriculum this is the **capstone rung** — the Kubernetes & DevOps
track that sits on top of the Linux / Git / Docker foundations. It is aimed at the
advanced learner (roughly 14+), not the absolute beginner.
---
## Quick Start
**Prerequisite:** [Docker](https://docs.docker.com/get-docker/)
```bash
docker run -itd --name kubekosh --privileged -p 7554:80 ced4568/kubekosh:latest
```
Open **http://localhost:7554** and wait ~30s for the *Cluster Ready* indicator to turn green.
> `--privileged` is required — K3s needs kernel namespaces and cgroups.
> **Do not expose this container directly to the public internet.** It is a teaching
> sandbox. Multi-student exposure is handled by the deployment layer below, not by
> publishing the raw container.
### Persist Progress
```bash
docker run -itd --name kubekosh --privileged -p 7554:80 \
-v <host_dir>:/data ced4568/kubekosh:latest
```
Progress is stored in SQLite at `/data/progress.db`. Mount a host directory to `/data`
to keep progress across restarts — one volume per student.
### Build From Source
```bash
docker build -t kubekosh .
# multi-platform
docker buildx build --platform linux/amd64,linux/arm64 -t kubekosh .
```
---
## What's Inside
| Bundle | Focus | Exam Mode |
| ------ | ----- | --------- |
| 🌱 Kubernetes Basics | Core concepts | 60 min |
| 🧑‍✈️ Kubernetes Administrator | CKA | 120 min |
| 🛠️ Kubernetes Developer | CKAD | 120 min |
| 🛡️ Kubernetes Security | CKS | 120 min |
**Scenario types**
- **Task** — hands-on challenge in the live terminal; **Validate** auto-checks cluster state.
- **MCQ** — multiple choice with a detailed explanation on submission.
Custom RootNode Academy scenarios live alongside upstream ones in `scenarios/data/` and are
grouped into bundles in `scenarios/bundles/`. See [`scenarios/SCHEMA.md`](scenarios/SCHEMA.md).
---
## Architecture
```mermaid
flowchart TD
subgraph Container["Single Docker Image (--privileged)"]
NGINX["nginx :80 — reverse proxy"]
FE["Frontend<br/>React + Vite + xterm.js"]
BE["Backend<br/>Node / Express + node-pty (WebSocket PTY)"]
K3S["K3s<br/>single-node in-container cluster"]
DB[("SQLite<br/>/data/progress.db")]
NGINX --> FE
NGINX --> BE
BE -->|kubectl / PTY| K3S
BE --> DB
end
Student["Learner browser"] -->|:7554| NGINX
```
| Component | Technology |
| --------- | ---------- |
| Frontend | React + Vite, `xterm.js` |
| Backend | Node.js / Express, `node-pty` WebSocket PTY |
| Cluster | K3s (single-node, in-container) |
| Proxy | nginx on port `80`, mapped to host `7554` |
| Storage | SQLite (`better-sqlite3`) at `/data/progress.db` |
Everything runs inside one image managed by `scripts/entrypoint.sh`.
---
## Multi-Student Deployment (RootNode Academy addition)
The upstream project assumes one learner, one container — this image is still exactly that,
unmodified except for a node-pty build fix (see `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` §1).
RootNode Academy multiplies it into a real platform via separate provisioning and gateway
layers rather than changing the image itself:
- `platform/provisioner/` — Postgres-backed environment registry, per-student Docker
networking, firewall-enforced egress, idle reaper (`docs/PHASE1_PLATFORM_FOUNDATION.md`).
- `platform/gateway/` — the only student-facing path to a Lab: resolves identity → environment
→ routing token, proxies HTTP/WebSocket into that one student's container, never a raw
container IP/port (`docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md`).
- The student flow (roster login → curriculum dashboard → Launch Lab → Lab session →
Reset/Recreate) lives in `frontend/src/platform/` — a small addition alongside the existing,
unmodified Lab UI, not a rewrite of it.
**Never publish the raw privileged container** — front it with isolation (that's what the
Gateway is). The one-container `docker run --privileged -p 7554:80 ...` command above stays
supported for local development and scenario authoring; it is not how this gets deployed to
real students.
Key properties of the platform (not this image):
- One container **per student**, its own Docker network, its own firewall chain — never a
shared cluster between learners. Verified with two simultaneous students: separate clusters,
separate progress, no cross-access.
- Progress/identity live outside the disposable container (a persistent identity instance
running this same image, currently SQLite-backed, moving to the Platform's PostgreSQL in a
later phase) — destroying or recreating a student's Lab never loses their academic record.
- CPU/memory/PID/storage limits are enforced per container, config-driven.
- Egress is restricted to an allowlisted proxy at the firewall layer, not left open.
- Idle/abandoned environments are automatically cleaned up, and a student can launch a fresh
one and keep going without losing progress.
Production topology and what's built vs. not yet: `docs/LAB_ARCHITECTURE.md`,
`docs/PHASE1_PLATFORM_FOUNDATION.md`, `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md`.
---
## Repository Layout
```
scenarios/
├── data/ # one JSON file per scenario -> <scenario-id>.json
├── bundles/ # one JSON file per bundle -> <bundle-id>.json
└── SCHEMA.md # full schema reference
backend/ server.js # Express API + WebSocket PTY
frontend/ src/ # React + Vite SPA
scripts/ entrypoint.sh # container startup (k3s -> API -> nginx)
nginx.conf # reverse-proxy config
```
---
## Authoring Scenarios
Each scenario is one JSON file in `scenarios/data/`; each bundle is one JSON file in
`scenarios/bundles/`. See [`scenarios/SCHEMA.md`](scenarios/SCHEMA.md) for the full schema.
**Task checklist**
- `validation.commands` — idempotent `kubectl` only
- `setup_commands` / `teardown_commands` — `kubectl` or native Ubuntu only
**MCQ checklist**
- `correct_option` must match an `options[].id`
- always include an `explanation`
Hot-reload while authoring (no rebuild):
```bash
docker run --rm -itd --privileged -p 7554:80 --name kubekosh \
-v $PWD/scenarios:/app/scenarios ced4568/kubekosh:latest
curl -X POST http://localhost:7554/api/cache/reload
```
---
## License
Apache 2.0 — see [`LICENSE`](LICENSE). Original work © the `zeborg/kubekosh` authors.
Modifications © RootNode Academy / Chase Dumphord (ced4568).