docs(phase2b): document admin and platform-data architecture
New docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md covers the Platform API split, Postgres schema and migration guarantees/rollback, the session cookie architecture (including the whoami-routing-bug fix), the admin portal, the API/security boundary, known limitations, and Phase 3+ future work. Updates ENGINEERING_SOP.md §8, LAB_ARCHITECTURE.md, README.md, and platform/README.md to reflect Platform API as the sole Provisioner caller and Postgres as authoritative for identity/progress as of Phase 2B.
This commit is contained in:
@@ -134,11 +134,18 @@ 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`).
|
||||
- `platform/api/` — lightweight Platform API: students, cohorts, curriculum, progress,
|
||||
scenario attempts, student/admin sessions, instructor/admin control plane. No k3s, no
|
||||
`--privileged` (`docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md`).
|
||||
- `platform/gateway/` — the only student-facing path to a Lab: resolves identity from a
|
||||
server-controlled session cookie (not a client-suppliable header/param), proxies
|
||||
HTTP/WebSocket into that one student's container, never a raw container IP/port
|
||||
(`docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md`, session model in
|
||||
`docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md` §3).
|
||||
- The student flow (roster login → curriculum dashboard → Launch Lab → Lab session →
|
||||
Reset/Recreate) lives in `frontend/src/platform/` — a small addition alongside the existing,
|
||||
Reset/Recreate) lives in `frontend/src/platform/`; the instructor/admin portal
|
||||
(cohorts, students, classroom "Prepare Labs," environment controls) lives in
|
||||
`frontend/src/admin/` at the `/admin` route — both small additions 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
|
||||
@@ -151,9 +158,14 @@ 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.
|
||||
- Progress/identity live outside the disposable container, in the Platform API's own
|
||||
PostgreSQL (authoritative as of Phase 2B) — destroying or recreating a student's Lab never
|
||||
loses their academic record. Verified with a full class-sized (3-student) cohort workflow:
|
||||
provision, complete different scenarios, reset one student's Lab, destroy all Labs after
|
||||
class — progress survives every step.
|
||||
- Instructors manage cohorts, students, curriculum assignment, and classroom Lab prep
|
||||
(provision an entire cohort before class, verify readiness, destroy Labs after) from
|
||||
`/admin` — no manual database, Docker, or Kubernetes access needed.
|
||||
- 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
|
||||
|
||||
@@ -171,13 +171,15 @@ section is just the map.
|
||||
environment per student — its own Docker network, its own container running the existing,
|
||||
unmodified Lab image, its own firewall-enforced egress. Postgres-backed registry
|
||||
(`environments`, `environment_events`, `lab_hosts`), idle reaper, startup reconciliation.
|
||||
- **What it is not:** directly reachable by a student, even now — the Gateway (§9) sits in
|
||||
front of it and is the only caller. Still exercised directly via its internal API
|
||||
(shared-secret auth) or `platform/provisioner/test/run.js` for isolated Provisioner testing.
|
||||
- **Where identity lives:** unchanged from §5 — `students`/`progress` are still on the Lab
|
||||
image's own SQLite. `environments.student_id` is deliberately opaque (no FK) until a later,
|
||||
separate migration puts roster/progress on the same Postgres instance as the environment
|
||||
registry.
|
||||
- **What it is not:** directly reachable by a student or the Gateway anymore — as of Phase 2B,
|
||||
the Platform API (§9) is the *only* caller (student launch/reset AND admin prepare/destroy/
|
||||
reset both go through it). Still exercised directly via its internal API (shared-secret
|
||||
auth) or `platform/provisioner/test/run.js` for isolated Provisioner testing.
|
||||
- **Where identity lives:** as of Phase 2B, Postgres is authoritative —
|
||||
`students`/`cohorts`/`progress`/`scenario_attempts` live in the same `rootnode_platform`
|
||||
database as `environments`, owned by the Platform API's own migrations. See §9.
|
||||
`environments.student_id` is still a plain opaque reference, not a real FK — that's a
|
||||
deliberate, documented choice (docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md §2), not an oversight.
|
||||
- **Local dev:** `platform/provisioner/docker-compose.dev.yml` runs Postgres + the egress
|
||||
proxy + the Provisioner on one machine — the documented Phase 1 dev exception. Production
|
||||
is a two-VM Proxmox split (Platform VM / Lab Host VM); the dev compose file is explicitly
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# RootNode Academy — Ephemeral Lab Architecture
|
||||
|
||||
Status: **Phase 1 (platform foundation) + Phase 2A (Gateway + student flow) IMPLEMENTED,
|
||||
2026-08-18** — see `docs/PHASE1_PLATFORM_FOUNDATION.md` and
|
||||
`docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` for the concrete schema/API/test results, and
|
||||
`platform/provisioner/README.md` for how to run the stack. This document remains the
|
||||
architecture record for the full ephemeral-lab direction; instructor dashboard, production
|
||||
auth/SSO, and production Proxmox deployment are still future work (Phase 2B — see the Phase 2A
|
||||
doc's §9). Supersedes the namespace-isolation
|
||||
Status: **Phase 1 (platform foundation) + Phase 2A (Gateway + student flow) + Phase 2B
|
||||
(Platform API split, Postgres academic data, server sessions, admin portal) IMPLEMENTED,
|
||||
2026-08-18** — see `docs/PHASE1_PLATFORM_FOUNDATION.md`, `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md`,
|
||||
and `docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md` for concrete schema/API/test results, and
|
||||
`platform/README.md` for how to run the stack. This document remains the architecture record
|
||||
for the full ephemeral-lab direction; production OIDC/SSO, per-instructor accounts, and
|
||||
production Proxmox deployment are still future work (Phase 3+ — see the Phase 2B doc's §13).
|
||||
Supersedes the namespace-isolation
|
||||
(`shared_namespace`/`ephemeral_cluster`) approach that was in flight before this redirect — see
|
||||
`scenarios/SCHEMA.md`'s `isolation_type` note for the pointer back here.
|
||||
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
# RootNode Academy — Phase 2B: Admin Portal & Platform Data
|
||||
|
||||
Status: **IMPLEMENTED and end-to-end tested, 2026-08-18.** Builds on
|
||||
`docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` (Phase 2A milestone
|
||||
`42dfb1c31b599d5e86b64915beb4b871b2e91055`). This doc covers: the platform/lab split, the
|
||||
Postgres academic schema and SQLite migration, server-controlled sessions, and the
|
||||
instructor/admin control plane.
|
||||
|
||||
---
|
||||
|
||||
## 1. Split: Platform API vs. Lab image
|
||||
|
||||
Phase 2A's "identity" instance was a full, `--privileged` Lab image booting an unused k3s
|
||||
cluster just to serve roster/curriculum/progress APIs. Phase 2B replaces it with
|
||||
`platform/api/` — a plain Node/Express service: no k3s, no `--privileged`, no terminal
|
||||
runtime, no nginx (serves the built frontend directly via `express.static`). Startup is
|
||||
instant instead of a 1-2 minute k3s cold start — directly observable in testing (see §8).
|
||||
|
||||
The Lab image (`Dockerfile`, `backend/server.js`, `scripts/entrypoint.sh`) is **completely
|
||||
unchanged in this phase** — it's still the disposable-per-student, nested-k3s, terminal +
|
||||
scenario-execution engine, exactly as Phase 1/2A built it. Platform API never runs
|
||||
setup/validate/teardown commands itself; it only relays outcomes the Lab already produced
|
||||
(see §7, "Progress relay," carried over from Phase 2A and extended).
|
||||
|
||||
Platform API owns: students, cohorts, enrollments, curriculum assignments, progress, scenario
|
||||
attempts, student/admin session identity, and — new in Phase 2B — **all** Provisioner calls,
|
||||
both student-initiated (launch/reset) and admin-initiated (prepare-labs, destroy-labs, reset,
|
||||
destroy). The Gateway no longer calls the Provisioner directly at all; it proxies
|
||||
`/api/environment/*` and `/api/admin/*` to Platform API, which is now the single place that
|
||||
holds the Provisioner's shared secret.
|
||||
|
||||
---
|
||||
|
||||
## 2. PostgreSQL academic schema
|
||||
|
||||
New tables in the existing `rootnode_platform` database (same instance the Provisioner's
|
||||
`lab_hosts`/`environments`/`environment_events` already live in — different migration
|
||||
directory, same Postgres):
|
||||
|
||||
- `students` — `student_id` stays the same opaque TEXT identifier used everywhere else
|
||||
(no breaking change to `environments.student_id`, which remains a plain reference with no
|
||||
FK, exactly as Phase 1 designed it).
|
||||
- `cohorts` / `cohort_enrollments` — replaces the old single `students.cohort` TEXT column
|
||||
with a real many-to-many model (a student can move between cohorts over time; the old
|
||||
model couldn't represent that at all).
|
||||
- `curriculum_assignments` — records which curriculum path a cohort or student is on. Only
|
||||
one path exists today (`rootnode-path`) — this table exists so assigning it is a real,
|
||||
auditable instructor action, not because multiple paths exist yet.
|
||||
- `progress` — same shape as the SQLite table it replaces.
|
||||
- `scenario_attempts` — **new capability**, not a migration of anything that existed before.
|
||||
One row per validate/answer outcome (result, validation count, timestamp, which
|
||||
environment), giving real per-attempt history instead of just an aggregate status +
|
||||
counter. `hint_count` is present but always NULL — the existing Lab engine's hints are
|
||||
static UI content only (`frontend/src/components/ScenarioPanel.jsx`), nothing server-side
|
||||
ever records a hint view. Documented here rather than inventing a number; the column is
|
||||
ready if hint-view tracking is ever added to the Lab engine.
|
||||
- `exam_sessions` — historical import of the old SQLite `sessions` table. **Scope note:**
|
||||
exam mode's *live* session bookkeeping (start/shuffle/submit/abandon) stays Lab-image-local
|
||||
for Phase 2B — productionizing it would need the same relay pattern progress got in Phase
|
||||
2A, and it isn't exercised by the Phase 2B acceptance test. This table makes historical exam
|
||||
records survive the migration and visible to instructors; it isn't written to by new exam
|
||||
sessions yet. Flagged as a real gap, not silently ignored — see §13.
|
||||
|
||||
## 3. SQLite → Postgres migration
|
||||
|
||||
`platform/api/migrate-sqlite.js`. Run once as the cutover:
|
||||
|
||||
```bash
|
||||
SQLITE_PATH=/path/to/progress.db PLATFORM_DB_URL=postgres://... node migrate-sqlite.js
|
||||
```
|
||||
|
||||
- **Backs up first, unconditionally** — copies the SQLite file to
|
||||
`<path>.backup-<timestamp>` before reading anything. The backup is never deleted or
|
||||
modified by the script.
|
||||
- **Idempotent** — every write uses `ON CONFLICT`; re-running re-syncs from the SQLite source
|
||||
rather than duplicating or erroring.
|
||||
- **Never silently drops data** — prints and writes a JSON report
|
||||
(`migration-report-<timestamp>.json`) with exact counts (students, progress rows,
|
||||
exam_sessions, cohorts created, enrollments created) and anything that couldn't be read
|
||||
(e.g. a missing table), with the reason.
|
||||
- **Preserves the old `students.cohort` column** as a real cohort + enrollment row instead of
|
||||
discarding it.
|
||||
- **Rollback:** Postgres only becomes authoritative once this has run and the Platform API is
|
||||
pointed at it — nothing here modifies or deletes the SQLite source. To roll back: stop
|
||||
Platform API, point student/instructor traffic back at a SQLite-backed service (the old
|
||||
Phase 2A "identity" image is unmodified and still buildable/runnable — see
|
||||
`docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md`), and restore from the backup file if the live
|
||||
SQLite file was somehow touched (it shouldn't have been — the migration is read-only against
|
||||
it).
|
||||
|
||||
See the final report for actual migrated record counts from this implementation's test run.
|
||||
|
||||
---
|
||||
|
||||
## 3. Student session — productionized ownership
|
||||
|
||||
Phase 2A's `?sid=` was explicitly pilot-only: a client-supplied query parameter, trivially
|
||||
changeable. Phase 2B replaces it with a server-controlled session:
|
||||
|
||||
- `POST /api/login {student_id}` (unchanged trigger — pick-your-name, no password, still
|
||||
pilot-grade identity, stated as such) creates a session server-side
|
||||
(`platform/api/src/sessions.js`, in-memory, 8h TTL) and returns an opaque, CSPRNG token as
|
||||
an `HttpOnly`, `SameSite=Lax` cookie (`rn_session`). The browser cannot read or fabricate
|
||||
this value.
|
||||
- Platform API derives `student_id` **only** from this cookie
|
||||
(`studentSession` middleware) — never from a header or query parameter a client controls.
|
||||
- The Gateway resolves "who is this browser" by calling Platform API's
|
||||
`GET /api/session/whoami` (forwarding the cookie), then compares that student_id against
|
||||
the environment's actual owning `student_id` (from the Provisioner, via Platform API) —
|
||||
**on every proxied Lab request, HTTP and WebSocket, not just the initial navigation.** A
|
||||
short-TTL cache (`SESSION_CACHE_TTL_MS`, 5s) keeps the extra round trip cheap.
|
||||
- Changing the token, URL, query parameter, or environment ID in the browser has **no effect**
|
||||
on who the Gateway thinks is asking — that's determined entirely by the unforgeable cookie.
|
||||
Verified by testing: a valid session for Student A requesting Student B's `/lab/<token>/`
|
||||
gets **403**, not just an unset-parameter fallback.
|
||||
|
||||
**Explicitly not production SSO/OIDC.** Sessions are in-memory (a Platform API restart logs
|
||||
everyone out — acceptable for a pilot/classroom tool, not silently ignored, see §13), and
|
||||
login is still pick-your-name with no password. Production OIDC/Google Workspace SSO remains
|
||||
Phase 3+ (§13/§14).
|
||||
|
||||
### Routing-hint cookie — a real bug found while building this
|
||||
|
||||
The Gateway's unprefixed fallback route (needed so the *unmodified* Lab UI's root-relative
|
||||
`/api/scenarios`, `/shell-ws`, etc. resolve correctly once a browser is inside `/lab/<token>/`
|
||||
— see Phase 2A's cookie design) initially used "does this session currently own a running
|
||||
environment" as the signal for "should I try routing this to the Lab." That's wrong: it's
|
||||
true on the *dashboard* too, not just inside the lab page, and it hijacked
|
||||
`GET /api/session/whoami` (and would have hijacked `/api/environment/*`, `/api/admin/*`,
|
||||
`/api/roster*`) to the student's Lab container — which doesn't have those routes at all —
|
||||
whenever a student had ever launched a lab. Confirmed by testing (whoami returned the SPA's
|
||||
`index.html` instead of JSON).
|
||||
|
||||
Fixed with an explicit allowlist (`LAB_OWNED_PATHS` in `platform/gateway/src/server.js`) of
|
||||
the specific unprefixed paths the Lab UI actually calls
|
||||
(`/api/scenarios*`, `/api/bundles`, `/api/sessions*`, `/api/cache*`, `/api/health`,
|
||||
`/shell-ws`, `/assets/*`) — everything else always goes to Platform API regardless of whether
|
||||
the student has a running lab. `LAB_ACTIVE_COOKIE` (set only on an actual `/lab/<token>/`
|
||||
visit) is a secondary gate on top of that, not the sole signal.
|
||||
|
||||
---
|
||||
|
||||
## 4-9. Admin/Instructor portal
|
||||
|
||||
New `/admin` route in the same frontend bundle (`frontend/src/admin/`) — a separate React tree
|
||||
from the student Platform Shell, gated by its own login (`rn_admin` session cookie, completely
|
||||
separate cookie/token namespace from `rn_session` — a student session can never call an admin
|
||||
route and vice versa).
|
||||
|
||||
**Dashboard** (`AdminDashboard.jsx`) — active cohorts, total students, Labs by state
|
||||
(running/provisioning/error/idle), recent completions, recent environment failures. No
|
||||
Prometheus/Grafana — this is "is class ready / is anything on fire," not an observability
|
||||
platform (explicit non-goal).
|
||||
|
||||
**Cohorts** (`CohortsView.jsx`) — create/edit, roster (add/view students), curriculum
|
||||
assignment, per-stage progress bars aggregated across the cohort, and the classroom
|
||||
preparation workflow:
|
||||
|
||||
- **Prepare Labs** — finds enrolled active students, provisions anyone without a usable
|
||||
environment. Runs concurrently (`Promise.allSettled`) — sequential would make a real cohort
|
||||
prep take the better part of an hour, since each provision is a genuine k3s cold start.
|
||||
Returns a per-student readiness report.
|
||||
- **Destroy Cohort Labs** — destroys every enrolled student's environment. Academic progress
|
||||
lives entirely in Postgres, untouched by this — by construction, not by a separate
|
||||
safeguard, since the destroy path never touches `students`/`progress`/`scenario_attempts`.
|
||||
|
||||
**Students** (`StudentsView.jsx`) — per-student curriculum progress, recent attempts
|
||||
(scenario, result, validation count, timestamp), current environment status, and controls:
|
||||
provision / reset-recreate / destroy / view event history.
|
||||
|
||||
**Admin auth** (`platform/api/src/admin-auth.js`) — a single instructor credential
|
||||
(`ADMIN_PASSWORD` env var, the same secret-injection channel every other component here
|
||||
uses), verified via `scrypt` + `timingSafeEqual` rather than a plain string comparison — no
|
||||
plaintext password ever sits in a database or gets logged. Explicitly not OIDC; documented as
|
||||
Phase 3+ future work.
|
||||
|
||||
---
|
||||
|
||||
## 10-12. API routes, security boundary
|
||||
|
||||
See the route table in the final report. Boundary preserved throughout:
|
||||
|
||||
```
|
||||
Student browser → Gateway → Platform API → Provisioner → Lab Host
|
||||
Admin browser → Gateway → Platform API → Provisioner → Lab Host
|
||||
```
|
||||
|
||||
Browser JavaScript never reaches the Provisioner directly in either path — Platform API is
|
||||
now the *only* thing holding `PROVISIONER_SHARED_SECRET`.
|
||||
|
||||
---
|
||||
|
||||
## 13. Known limitations
|
||||
|
||||
- **Sessions are in-memory, single-process.** A Platform API restart invalidates every student
|
||||
and admin session. Acceptable at pilot/classroom scale; would need a shared store (Postgres
|
||||
or Redis) before this runs across multiple Platform API instances.
|
||||
- **Exam mode is not fully productionized** — live exam sessions stay Lab-local; only
|
||||
historical records migrate to Postgres. See §2.
|
||||
- **`identity`'s Phase 2A `network_mode: bridge` requirement no longer applies** — Platform
|
||||
API replaces it entirely and doesn't run k3s, so that whole class of problem is gone, not
|
||||
just documented around.
|
||||
- **Admin credential is a single shared password**, not per-instructor accounts — fine for a
|
||||
single-instructor pilot, not a multi-instructor deployment model.
|
||||
- **`?sid=` is gone entirely** (Phase 2A's mechanism) — nothing reads it anymore; a stale
|
||||
bookmarked URL containing it is harmless, the parameter is simply ignored.
|
||||
|
||||
## Future (Phase 3+)
|
||||
|
||||
Production OIDC/SSO, per-instructor accounts, full students/progress historical fidelity for
|
||||
exam mode, shared session store for horizontal scaling, everything already listed as
|
||||
out-of-scope in Phase 2A/2B (parent portal, billing, capstone engine, production Proxmox,
|
||||
full observability, multi-host scheduling, public enrollment).
|
||||
+29
-19
@@ -6,42 +6,52 @@ Start here, then go to the component you need:
|
||||
- `provisioner/` — Postgres-backed environment registry and lifecycle. Design:
|
||||
`docs/LAB_ARCHITECTURE.md`, `docs/PHASE1_PLATFORM_FOUNDATION.md`. Operational reference:
|
||||
`provisioner/README.md`.
|
||||
- `api/` — the lightweight Platform API: students, cohorts, enrollments, curriculum, progress,
|
||||
scenario attempts, student/admin sessions, and the instructor/admin control plane. No k3s, no
|
||||
`--privileged` — this replaced Phase 2A's "identity" instance, which was a full Lab image
|
||||
booting an unused k3s cluster just to serve these APIs. Design:
|
||||
`docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md`. Operational reference: `api/README.md`.
|
||||
- `gateway/` — the only student-facing path to a Lab (HTTP + WebSocket proxy, environment-aware
|
||||
routing, student identity glue). Design: `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md`.
|
||||
routing derived from a server-controlled session — not a client-suppliable header/param).
|
||||
Design: `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` (routing), `docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md` §3 (session ownership).
|
||||
- `egress-proxy/` — shared allowlisting proxy every Lab's outbound traffic passes through.
|
||||
- `docker-compose.dev.yml` — the full stack (Postgres, egress proxy, Provisioner, a persistent
|
||||
"identity" instance, Gateway) on one machine. This is the Phase 1/2A **local dev exception**
|
||||
— production is the two-VM Proxmox split described in `docs/LAB_ARCHITECTURE.md` §1.
|
||||
- `docker-compose.dev.yml` — the full stack (Postgres, egress proxy, Provisioner, Platform API,
|
||||
Gateway) on one machine. This is the Phase 1/2A/2B **local dev exception** — production is
|
||||
the two-VM Proxmox split described in `docs/LAB_ARCHITECTURE.md` §1.
|
||||
|
||||
## Running the full stack locally
|
||||
|
||||
```bash
|
||||
cd platform
|
||||
echo "PROVISIONER_SHARED_SECRET=$(openssl rand -hex 32)" > .env
|
||||
echo "ADMIN_PASSWORD=$(openssl rand -hex 12)" >> .env
|
||||
docker compose -f docker-compose.dev.yml build
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
Wait for `identity` to boot (it runs a real k3s cluster, same as any Lab — check with
|
||||
`curl http://localhost:7554/api/health`), then the whole platform is reachable at
|
||||
`http://localhost:4200` (the Gateway) — that's the one origin a student's browser would use.
|
||||
No k3s boot to wait for at this layer anymore (Platform API starts instantly) — the whole
|
||||
platform is reachable at `http://localhost:4200` (the Gateway) right away: students at `/`,
|
||||
instructors at `/admin`.
|
||||
|
||||
## Tests
|
||||
|
||||
- `provisioner/test/run.js` — fast, isolated Provisioner tests (network/container/firewall
|
||||
mechanics, no Gateway/identity involved). Run against `provisioner/docker-compose.dev.yml`.
|
||||
- `test/e2e-phase2a.js` — slow, full-stack tests against the compose file above: roster →
|
||||
login → curriculum → launch → Gateway routing → cross-student rejection → progress relay →
|
||||
reset. Real k3s boots, budget several minutes. Run whenever the Gateway, Provisioner API
|
||||
surface, or the identity backend's progress/roster/curriculum routes change.
|
||||
mechanics, no Gateway/Platform API involved). Run against `provisioner/docker-compose.dev.yml`.
|
||||
- `test/e2e-phase2a.js` — student-flow regression through real session cookies: roster → login
|
||||
→ curriculum → launch → Gateway routing → cross-student rejection → progress relay → reset.
|
||||
- `test/e2e-phase2b.js` — the classroom acceptance workflow: instructor creates a cohort, adds
|
||||
students, assigns curriculum, Prepare Labs (concurrent), students complete different
|
||||
scenarios, instructor resets one, instructor destroys all cohort Labs after class — progress
|
||||
survives every step. Needs `ADMIN_PASSWORD` set (see `platform/.env`).
|
||||
|
||||
Neither substitutes for the manual real-Lab-image checks (node-pty, terminal WebSocket,
|
||||
`kubectl`, scenario validation) in `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` §8 — those need a
|
||||
browser-equivalent client and aren't automated here.
|
||||
Both e2e suites are slow (real k3s boots) and need the full stack up. Neither substitutes for
|
||||
the manual real-Lab-image checks (node-pty, terminal WebSocket, `kubectl`, scenario validation)
|
||||
in `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` §8 — those need a browser-equivalent client and
|
||||
aren't automated here.
|
||||
|
||||
## Known limitations
|
||||
|
||||
See `provisioner/README.md` (Phase 1) and `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` §9
|
||||
(Phase 2A) for the full, honest lists — including the `identity` service's `network_mode:
|
||||
bridge` requirement and its unused-k3s resource cost, both flagged as real, not swept under
|
||||
the rug.
|
||||
See `provisioner/README.md` (Phase 1), `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` §9 (Phase
|
||||
2A), and `docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md` §13 (Phase 2B) for the full, honest lists —
|
||||
including in-memory sessions, the single shared admin credential, and exam mode's live
|
||||
bookkeeping staying Lab-local.
|
||||
|
||||
Reference in New Issue
Block a user