xPLN is a courseware management application built on top of the xPLA framework. It provides a web interface for organizing courses, pages, and learning activities, with real-time activity execution via WebSockets.
┌──────────────────────────────────────────────┐
│ FastAPI server (port 9753) │
│ │
│ /api/* REST + WebSocket endpoints │
│ /a/* Activity asset serving │
│ /static/* Shared static files │
│ /_next/* Built frontend assets │
│ /* SPA fallback (index.html) │
│ │
│ SQLite + Alembic │ xPLA ActivityRuntime │
│ React (static) │ WebSocket EventBus │
└──────────────────────────────────────────────┘
Backend (app.py): FastAPI server handling CRUD operations, activity execution, WebSocket connections, and serving the built frontend.
Frontend (frontend/): Next.js app (static export) providing the UI — course/page/activity management with drag-and-drop reordering. Built to frontend/out/ and served by FastAPI.
Database (db.py): SQLite database at dist/xpln.db, with Alembic migrations in migrations/. Migrations run automatically on startup.
Defined in models.py:
- User — account with email + pbkdf2-hashed password
- UserSession — opaque session token backing the
xpln_sessioncookie - Course — top-level container owned by a single user (
owner_id); private to its owner - Page — belongs to a course, contains activities
- PageActivity / CourseActivity — instances of an activity type on a page or on the course dashboard
Field persistence (learner state, scores, etc.) is handled by field_store.py, which implements the xPLA FieldStore interface using three SQLite tables for scalar values, log entries, and sequence counters.
Email/password auth with a DB-backed session token. See auth.py and views/auth.py for the endpoints (/api/auth/signup, /api/auth/login, /api/auth/logout, /api/me). All course/page/activity routes require an authenticated user, and each course is private to its owner_id.
The REST API and WebSocket endpoint are defined in app.py.
The Next.js app lives in frontend/. Key areas:
- Routes (frontend/src/app/): single catch-all route with client-side routing to home (courses list), course detail (pages), page detail (activities), and activities management
- Components (frontend/src/components/): course/page/activity lists, sidebar navigation,
<xpl-activity>web component wrapper - API client (frontend/src/lib/api.ts): typed fetch wrappers for all backend endpoints
From the project root:
npm install src/xpla/notebook/frontend/
make notebook-frontend-build # Build the frontend static export
make notebook-server # FastAPI server on port 9753Then open http://localhost:9753.
For active frontend development with HMR, you can run cd src/xpla/notebook/frontend && npm run dev alongside the FastAPI server.
- Activity types are discovered by scanning
samples/for directories containing amanifest.json - When a user opens an activity, the backend creates an xPLA
ActivityRuntimethat loads the manifest and manages sandbox execution - The frontend renders a
<xpl-activity>custom element (defined in src/xpla/static/js/xpla.js) which connects via WebSocket - Actions and events flow between the client script and the WASM sandbox through the WebSocket, with permission-based filtering via the
EventBus
Permission levels: view (read-only), play (interactive, for learners), edit (authoring).