Skip to content

Spoke Plus Architecture

1. System Overview

Spoke Plus is implemented as a two-service web platform with strict separation between presentation, API, and data access:

  • Admin/Web Client: Next.js 14 application (App Router) running independently on port 3001.
  • API Service: Node.js + Express service running on port 3000.
  • Data Platform: Supabase for Auth, Postgres, and Storage.

The web client authenticates with Supabase and calls the Express API for all privileged operations. The Express API is the only runtime component that performs administrative reads/writes against Postgres using the service role client. This preserves a clean trust boundary between browser and backend.

2. Runtime Components

2.1 Node.js + Express API

The API is defined in app.js and mounted by server.js.

  • JSON HTTP API with CORS and route modularization.
  • Route groups:
  • /status
  • /admin (students, content engine, dashboard, system)
  • Global contract helper:
  • Success: { ok: true, data }
  • Failure: { ok: false, error: { code, message } }

All admin route groups enforce middleware stacks for rate limiting, admin authorization, and logging.

2.2 Next.js 14 (App Router)

The web application is built with Next.js 14 (next@^14.2.0) and uses the app/ directory routing model.

  • Client-side API integration through web/lib/api.ts.
  • Supabase browser client instantiated with NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY.
  • Auth token forwarding from browser to Express API through Authorization: Bearer <token>.

2.3 Supabase (Auth + Postgres + Storage)

Spoke Plus uses Supabase as the managed backend platform:

  • Auth: user identity and access tokens.
  • Postgres: application data (profiles, progression, content engine, analytics views).
  • Storage: available as part of Supabase platform integration (URLs referenced in content model and AI pipeline planning).

The backend service connects with SUPABASE_SERVICE_ROLE_KEY; frontend uses only anon/public key.

2.4 PM2 Process Structure

System monitoring expects two PM2 processes:

  • spokeplus (API)
  • spokeplus-web (web service)

System endpoints query pm2 jlist and stream tails from PM2 log files:

  • spokeplus-out.log
  • spokeplus-error.log
  • spokeplus-web-out.log
  • spokeplus-web-error.log

2.5 Port Separation

Current runtime defaults and conventions:

  • 3000: Express API (PORT, default 3000)
  • 3001: Web frontend allowed by API CORS default origin (http://localhost:3001)

This explicit split decouples frontend hosting/runtime lifecycle from API lifecycle.

3. Security Model

3.1 Key Usage Boundary

  • Frontend: uses ANON key (NEXT_PUBLIC_SUPABASE_ANON_KEY) only.
  • Backend: uses SERVICE_ROLE key (SUPABASE_SERVICE_ROLE_KEY) only.

No frontend component should hold or use service role credentials.

3.2 Admin Authorization (requireAdmin)

All admin routes (except explicit bootstrap path) enforce requireAdmin:

  1. Validate bearer token format.
  2. Resolve user from Supabase Auth (auth.getUser(token)).
  3. Check presence in admin_users table.
  4. Reject unauthorized/forbidden users with mapped API errors.

3.3 Role Separation

  • Admin membership is represented in admin_users.
  • Student queries explicitly exclude admin users.
  • Admin endpoints are segregated from student-facing data semantics.

3.4 Architectural Safety Rules (Current Policy)

  • No Server Actions for admin/data mutations.
  • No direct privileged DB mutations from frontend.
  • Express API is the only write path for admin-controlled operations.

4. Role Model

Student (Implemented)

  • Primary identity in auth.users.
  • Provisioned into user-domain tables via trigger (provision_user_defaults).
  • Has profile/progression/wallet/streak/session state.

Admin (Implemented)

  • Access controlled by admin_users table.
  • Required for /admin/* operations (post-bootstrap).
  • Explicitly filtered out from student listings and student-specific operations.

Future System Admin (Planned)

  • Higher-order admin governance role for admin lifecycle control.
  • Not currently implemented as a distinct runtime role type.

5. Database Structure Layers

5.1 User Provisioning Layer (Implemented)

  • Trigger on auth.users creates baseline records in:
  • user_profile
  • user_settings
  • user_wallet
  • user_xp
  • user_streak
  • RLS enablement and owner policies are provisioned idempotently for user-domain tables.

5.2 Linguistic Engine Layer (Implemented)

  • vocabulary, word_forms, word_tags, word_translations, word_assets, word_grammar, semantic_relations
  • sentences, sentence_tokens, sentence_tags, pos_colors, tts_assets
  • Lemma-first multilingual model with tokenized sentence grounding and reusable media layers.

5.3 Course Structure Layer (Implemented)

  • coursesunitsskillslessonslesson_content_map
  • Ordered hierarchy with indexes and constraints for deterministic traversal.

5.4 Skill Graph Layer (Planned)

  • Dependency graph and unlock orchestration are identified as roadmap capabilities.
  • Dedicated graph tables/logic are not yet present in current migrations.

5.5 Analytics Layer (Implemented + Extensible)

Views include:

  • vw_dashboard_metrics
  • vw_recent_activity
  • vw_session_summary
  • vw_practice_most_wrong
  • vw_practice_due_items
  • vw_wallet_overview
  • vw_user_streak

These power dashboard, reports, and learner diagnostics.

5.6 System Monitoring Layer (Implemented)

  • /admin/system/status
  • /admin/system/logs
  • /admin/system/logs/stream (SSE)

Includes PM2 status polling and sanitized log streaming.

6. API Design Principles

6.1 Response Contract

  • Success payloads return { ok: true, data }.
  • Failures return normalized code + message.

6.2 Error Mapping Standard

Frontend maps key statuses to product-safe UX messages:

  • 401: session expired
  • 403: no permission
  • 404: resource not found
  • 5xx/network: retry-oriented fallback

6.3 Endpoint Consistency

  • Admin domain endpoints are grouped by function (students/content/dashboard/system).
  • Existing architecture policy prohibits duplicate endpoint definitions.

6.4 Migration Idempotency

SQL migrations use if not exists, constraint existence guards, and upsert patterns where appropriate to support repeated execution safely.

7. Builder Architecture

The current builder model is editor-first and API-mediated:

  • Editor-first/local state: UI edits are staged in frontend state before persistence.
  • Optimistic interaction pattern: UI flows are designed for low-friction incremental updates.
  • Express-only writes: content and structure writes occur via /admin endpoints only.
  • No direct DB calls from browser for admin mutations.

This architecture supports scalable future features (skill graph, adaptive generation) while keeping current production writes centralized and auditable.