Commit Graph
46 Commits
Author SHA1 Message Date
ced4568andClaude Sonnet 5 c8dd33522f feat(provisioner): add disposable lab lifecycle
Internal-only service (platform/provisioner/) implementing the environment
lifecycle from docs/PHASE1_PLATFORM_FOUNDATION.md: provision -> running ->
(reset -> running | destroy -> destroyed), plus experimental suspend/resume
(decision #3 — never on the guaranteed path, never relied on for correctness).

- src/docker.js — the lab-host abstraction (correction D). Resolves a
  lab_hosts row into a connected dockerode client, unix:// socket or tcp://
  with TLS client-cert auth. Refuses to connect to a tcp:// endpoint with no
  TLS material configured rather than connecting unauthenticated. Validated
  against both a local socket and a real remote Docker-in-Docker TLS endpoint
  during testing — same code path either way, only the lab_hosts config
  differs, which is the entire point.

- src/lifecycle.js — provision/destroy/reset/suspend/resume orchestration,
  wired to docker.js + egress.js + the Postgres registry. Every Lab container
  is privileged (required for k3s, not avoidable), gets its own Docker network,
  no published host ports, deterministic naming (rootnode-lab-<short-id> /
  rootnode-lab-net-<short-id>), and a CSPRNG routing token. Includes
  reconcileOnStartup(), implementing the "don't trust stale state after a
  crash/restart" requirement from docs/LAB_ARCHITECTURE.md §8 — verified by
  killing a container out-of-band and confirming a Provisioner restart flags
  the mismatch instead of trusting the stale 'running' row.

  Found and fixed during testing: a provisioning failure (e.g. readiness
  timeout) left an orphaned container/network/firewall chain behind with only
  the DB row marked 'error'. provisionEnvironment's catch block now does
  best-effort teardown of whatever was already created before marking the row
  error — confirmed by deliberately failing a provision and checking no
  container/network/chain survived it.

- src/server.js — the internal HTTP API (X-Provisioner-Key shared-secret auth
  on every route, matching the instructor-passcode pattern already used for
  the roster endpoint). Input validation on student_id/environment_id,
  structured JSON errors with a request id, routing tokens redacted from every
  response.

- docker-compose.dev.yml — Postgres + egress-proxy + provisioner on one
  machine (the documented Phase 1 dev exception). provisioner and egress-proxy
  run with network_mode: host + NET_ADMIN/NET_RAW, which is what lets
  src/egress.js manipulate the host's DOCKER-USER chain without needing
  interactive host sudo.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-18 01:21:52 -05:00
ced4568andClaude Sonnet 5 62241d26d2 feat(platform-db): add lab host and environment registry
New Phase 1 Platform database — PostgreSQL, separate from the Lab image's own
SQLite (which keeps its current students/progress data untouched, decision #2 /
correction C in docs/LAB_ARCHITECTURE.md).

Schema (platform/provisioner/migrations/):
- lab_hosts: the abstraction that keeps Docker connectivity out of code —
  endpoint (unix:// or tcp://+TLS), enabled flag. Phase 1 ships one row.
- environments: environment_id (UUID), student_id (opaque external identifier,
  no FK — students table isn't in this database yet), lab_host_id (real FK),
  status, routing_token (CSPRNG via crypto.randomBytes, opaque), container/
  network identity, resource limits, lifecycle timestamps.
- environment_events: append-only audit trail, one row per lifecycle
  transition, never logs the routing token in plaintext (see
  lifecycle.js's redact()).

File-based migration runner (db.js) matching the check-before-alter idempotency
pattern already used in the Lab image's backend/server.js, applied on every
Provisioner boot.

Resource limits and the lab_hosts seed are config files
(config/resource-policy.yaml, config/lab-hosts.seed.yaml), not database tables —
there's no admin UI to manage them via a database yet, so a DB-backed policy
table would just be a write path nothing uses.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-18 01:21:31 -05:00
ced4568andClaude Sonnet 5 60fbf0c385 feat(curriculum): add RootNode learning path, student roster, and scenario isolation metadata
Pre-Phase-1 platform work, approved and tested earlier but not yet committed:

- Terminal banner: "RootNode Academy Lab — node: k8s-lab", upstream KubeKosh
  attribution left untouched in footer/README/LICENSE.
- RootNode curriculum mode: scenarios/rootnode-path.json (Foundation -> Workloads
  -> Networking -> Configuration -> Troubleshooting -> Real Ops -> Capstone),
  built from existing scenario content grouped by category, no new scenarios
  invented. New GET /api/curriculum/path endpoint.
- Student roster + per-student progress: instructor-roster, pick-your-name login
  (no password), students/environments-adjacent progress table migrated to a
  (student_id, scenario_id) composite key so practice progress and exam sessions
  are isolated per student instead of one global bucket. New /api/roster,
  /api/login endpoints.
- scenarios/data/*.json: added isolation_type metadata (shared_namespace vs
  ephemeral_cluster) from an audit of which scenarios execute cluster-scoped or
  node-level commands. This was Tier-1-isolation groundwork that got superseded
  same-day by the full-ephemeral-per-student architecture (see
  docs/LAB_ARCHITECTURE.md) — the field is kept as descriptive metadata only,
  per the note now in scenarios/SCHEMA.md. Not wasted: it's an accurate audit of
  which scenarios touch cluster-scoped resources, useful independent of isolation
  model.
- .gitignore added (node_modules/, frontend/dist/, *.db, /data/, .env) — none
  existed before; without it, npm installs during testing would have landed in
  a commit.

Known limitation still open: no frontend UI for any of this yet (login screen,
roster-add form, curriculum view) — API-only. No auth on the roster-add endpoint.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-18 01:21:16 -05:00
ced4568 6ddfb1cbde feat: add probe-port-mismatch and enforce-limits to rootnode-real-ops bundle 2026-08-17 12:45:16 -05:00
ced4568 e31b051f4e feat: update logo.svg for RootNode Academy branding 2026-06-21 03:34:56 -05:00
ced4568 f7cdb48833 feat: rebrand to RootNode Academy + add Real Ops scenario bundle
- Rename app title, header, tagline, footer, terminal tab to RootNode Academy
- Apply RootNode color palette (#39FF14 green, #00D4FF blue, GitHub-style darks)
- Add rootnode-runaway-monitor scenario (homelab NOC incident: oversized memory request)
- Add rootnode-real-ops bundle in terminal-green accent
- Update package name to rootnode-academy-ui
2026-06-21 03:27:46 -05:00
Abhinav Sinha d42aa779b1 feat(scenarios): add 10 new Kubernetes Basics scenarios
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-20 23:54:52 +05:30
Abhinav Sinha 6c3843cb90 fix(cluster): add API stability verification and k3s watchdog
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-19 22:11:43 +05:30
Abhinav Sinha ff1c61812d docs: update README.md with new screenshots
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-18 12:18:37 +05:30
Abhinav Sinha f2d535837c fix: remove reset button from scenario panel in exam mode
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-18 02:00:21 +05:30
Abhinav Sinha ec27b01531 refactor: replace window alerts with modals
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-18 01:49:10 +05:30
Abhinav Sinha 35dfeb7208 feat: add focus mode to maximize scenario panel
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-18 01:30:39 +05:30
Abhinav Sinha 09bd1b20da refactor(exam): flat sidebar list, weight badges, UI cleanup
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-18 01:11:19 +05:30
Abhinav Sinha 7e75c8ae02 fix: hide difficulty from scenario ui in exam mode
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-17 16:33:14 +05:30
Abhinav Sinha 935ab38f28 fix: ordered bundles sequence by id
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-17 16:23:44 +05:30
Abhinav Sinha 170b95aa35 fix: kubectl bash completion
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-17 15:09:39 +05:30
Abhinav Sinha f21fdc1730 fix: scenarios having solution in problem statement itself
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-16 16:47:16 +05:30
Abhinav Sinha 3e77495f4b fix: exam history button hover animation
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-16 16:35:36 +05:30
Abhinav Sinha 4acdb0df40 feat(exam): log time spent per scenario in exam report
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-16 16:29:50 +05:30
Abhinav Sinha 6766d772d1 feat(exam): add exam history and improve scenario timer
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-16 16:03:08 +05:30
Abhinav Sinha 5720b59a5f feat(exam): add timer for each scenario in exam mode
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-16 10:53:14 +05:30
Abhinav Sinha ab2403e914 feat: implement scenario caching and hot-reloading
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-15 15:28:53 +05:30
Abhinav Sinha 8489544981 refactor(server): replace execSync with execAsync
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-15 11:02:28 +05:30
Abhinav Sinha e7562b1b71 fix: incorrect container name in broken-deployment hint
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-15 10:59:28 +05:30
Abhinav Sinha 4603f8d938 chore: add security warning in README.md for privileged mode
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-15 08:03:23 +05:30
Abhinav Sinha 124e5276d4 refactor(scenarios): split monolithic configuration into individual files
- Split `scenarios/scenarios.json` into individual JSON files under `scenarios/data/` named `<scenario-id>.json`
- Split `scenarios/bundles.json` into individual JSON files under `scenarios/bundles/` named `<bundle-id>.json`
- Updated `backend/server.js` to dynamically load scenario and bundle files from their respective directories
- Updated documentation in `scenarios/SCHEMA.md` and `README.md` to reflect the new repository layout and contributor workflow

Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-15 07:52:47 +05:30
Abhinav Sinha 2104c5fea6 fix: globalDefault validation in priority-class-task
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-14 05:01:52 +05:30
Abhinav Sinha a3053c8b0c fix: validate successfulJobsHistoryLimit in cronjob-task
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-14 04:32:26 +05:30
Abhinav Sinha f1a35fec4f fix: memory request validation in resource-limits-task
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-14 03:30:09 +05:30
Abhinav Sinha 9ee1e5bd04 fix: incorrect configmap-volume hint command for verification
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-14 02:20:14 +05:30
Abhinav Sinha 71b65cb0af fix: resource-quota-ns default namespace and setup commands
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-10 11:59:34 +05:30
Abhinav Sinha 3f51e219b0 fix: teardown issues in 3 scenarios
1. pv-pvc-mount
2. node-tain-toleration
3. pod-disruption-budget-task

Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-10 11:51:08 +05:30
Abhinav Sinha 658296b30e fix: CrashLoopBackOff in cks-pod-security-context scenario
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-10 03:49:30 +05:30
Abhinav Sinha bc86618fd0 fix: CrashLoopBackOff in pod-security-context scenario
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-09 21:57:10 +05:30
Abhinav Sinha abcd8592b5 fix: execute teardown on active scenario's progress reset
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-09 16:50:34 +05:30
Abhinav Sinha 30fb302155 fix: replace quoted cmd with base64 in kubectl cp setup
Closes #10

Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-09 15:34:34 +05:30
Abhinav Sinha be1fc296ac fix: Incorrect Scenario Setup State For Completed Scenarios
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-09 13:28:57 +05:30
Abhinav Sinha a5ce787d1d fix: Fix a Broken Service Selector Setup & Hints
Closes #9

Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-09 13:23:29 +05:30
Abhinav Sinha 17150dc856 chore: Add screenshots in README.md
Signed-off-by: Abhinav Sinha <[email protected]>
2026-06-01 02:42:18 +05:30
Abhinav SinhaandGitHub 3040e0e775 Merge pull request #6 from zeborg/feat/new-scenarios
feat: Add 10 new scenarios
2026-05-31 04:39:22 +05:30
Abhinav Sinha 356d4ee326 feat: Add 10 new scenarios
Added new scenarios:

1. Kubernetes Basics:
  - [TASK] Annotating Kubernetes Resources
  - [TASK] Pod with Init Container
  - [MCQ] Pod Restart Policies
  - [MCQ] kubectl Output Formats

2. Kubernetes Administrator (CKA):
  - [TASK] Configure a PodDisruptionBudget
  - [TASK] Pod Priority and PriorityClass

3. Kubernetes Security (CKS):
  - [TASK] RuntimeClass for Sandboxed Workloads
  - [TASK] Write a Kubernetes Audit Policy
  - [TASK] Restrict Egress to Namespace
  - [TASK] Enforce Non-Root Container

Signed-off-by: Abhinav Sinha <[email protected]>
2026-05-31 04:20:10 +05:30
Abhinav Sinha df52217fc5 fix: Upgrade git-cliff action to v4
Signed-off-by: Abhinav Sinha <[email protected]>
2026-05-31 00:32:15 +05:30
Abhinav Sinha ff862ce6e8 fix: Docker Hub Release Action Write Permission
Signed-off-by: Abhinav Sinha <[email protected]>
2026-05-31 00:15:03 +05:30
Abhinav SinhaandGitHub ddbb1303eb Merge pull request #5 from zeborg/release-workflow
feat: Add `Release to Docker Hub` GitHub Action
2026-05-30 23:27:22 +05:30
Abhinav Sinha e999a8a857 feat: Add Release to Docker Hub GitHub Action
Implemented Docker Release action with GitHub workflows
and added release versioning with docker build args

Signed-off-by: Abhinav Sinha <[email protected]>
2026-05-30 23:08:48 +05:30
Abhinav SinhaandGitHub c26c0122ce Initial commit 2026-05-26 20:18:08 +05:30