System Architecture
Public content is public by default and served as prerendered static pages; anything that needs trust (accounts, progress, assessment, instructor and admin features) goes through the Worker API.
- Estimated time
- About 14 min
- Last verified
Browse all help topics
Skip to articleArchitecture at a glance
NTPU AI Learn splits the system into two paths. To read public content, the browser simply fetches pages that were prerendered at build time. Anything that needs trust, such as submitting an assessment, syncing progress, signing in or using instructor and admin features, goes to the Worker API, which validates and authorizes the request before it reads or writes the database.
- UnderstandStart with the big picture: system boundaries and relationships
- BuildGo deeper into structure: Container and its components
- OperateDeployment, operation and failure behavior
The read path needs no sign-in, makes no API call and never touches the database, so public content is fast and indexable. The interactive path is the only way to read or write learning data, and every request on it is validated and authorized inside the Worker. Answer keys and instructor-only content live only on the server and never appear on the public path.
Some interactive actions need more than a valid, authorized request. Before something like a course-completion certificate or a capability certificate is granted, the Worker also runs server-side eligibility rules that check real evidence (such as completed lessons and passed assessments, or an instructor's reviewed and passed judgment of submitted work) before anything is issued. Nothing is ever granted just because a form was submitted.
System Context
This diagram focuses on one thing: who uses NTPU AI Learn, and how the platform interacts with the outside world.
It lets someone approaching the platform for the first time quickly understand the system boundary and the overall picture.
Learner
Reads courses, completes learning and assessments
- Reads courses, completes learning and assessments leads to → NTPU AI Learn
Instructor
Teaches, and views sessions and learning results
- Teaches, views sessions and learning results leads to → NTPU AI Learn
Administrator
Manages the platform, sessions and permissions
- Manages the platform, sessions and permissions leads to → NTPU AI Learn
NTPU AI Learn
AI learning and practice platform
- Confirms sign-in identity leads to → Google sign-in
- Checks the submitter is human leads to → Cloudflare Turnstile
Google sign-in
One of the optional sign-in methods
Cloudflare Turnstile
Bot verification on public forms, enabled per form
Container Architecture
This diagram unpacks the single "NTPU AI Learn" box from System Context into the technical components that are actually deployed and run. It answers: what are the main execution and data components, what does each own, and how do they talk to each other.
Browser
Learner, instructor or admin
- Read public content leads to → Static Assets
- Interactive action (/api/) leads to → Worker API
Static Assets
Prerendered HTML, JS and CSS
Zone: Public
Worker API
Validation, identity, authorization, scoring
Zone: Server only
- Read and write leads to → D1 database
- Upload and signed download leads to → R2 file storage
D1 database
Accounts, sign-in sessions, progress, submissions, results
Zone: Persistent data
R2 file storage
Credential evidence files and protected teacher slide exports; file metadata kept in D1
Zone: Persistent data
Version-controlled content
Courses, Help and Docs, with stable IDs shared by both languages
- Generated at build time leads to → Static Assets
Server-side protected logic
Answer keys, speaker notes, role checks
Zone: Server only
- Deployed only with the Worker leads to → Worker API
How one action moves through the system
Two real examples: one that only reads public content, and one that needs server-side scoring and a database write.
A learner opens a module page of a course without signing in.
- 1The browser requests the page
For example, a module URL under /courses/…. - 2The Worker handles the URL
Legacy URLs are permanently redirected at this point, and every response gets security headers. - 3A prerendered page is returned
The complete HTML, CSS and JS generated at build time come from Static Assets. No API call, no database read, no sign-in. - 4The browser shows it and takes over
The content is readable immediately. Once JavaScript loads it adds interaction such as the language switch, self-checks and “Mark as complete”; by default progress stays in this browser only.
A learner in a course session takes the post-test using a session code and a learner code.
- 1Start the attempt
The browser sends a POST to an assessment API under /api/ with the session code and learner code (no sign-in needed). The Worker validates the input, confirms the session is open, creates an attempt record in D1 and returns the questions without answers. - 2Submit the answers
When the learner finishes, the browser sends another POST with the option chosen for each question. - 3Schema validation
The Worker first checks the request against its schema; a malformed request is rejected right away. - 4Check the attempt and session
The Worker finds the attempt and confirms the session is still open. An attempt that was already submitted gets its original result back and is never rescored. When bot verification is enabled, it is checked on the server here too. - 5Score on the server
After confirming every question is answered and every question exists, the Worker scores the attempt against answers that exist only on the server and summarizes performance by competency. - 6Write to D1
The score and each answer are written to the database with parameterized queries. - 7Return the result and update progress
The response carries the score and competency breakdown (a post-test also includes the learner’s own pre-test score). The page shows the result and marks the assessment complete in the browser’s progress.
Components
| Component | Responsibility | Technology | Trust level |
|---|---|---|---|
| Front end (in the browser) | Renders pages, handles interaction, keeps learning progress locally | React, TypeScript, Vite | Untrusted: runs on the user’s device, where anyone can inspect and change it |
| Static Assets | Serves prerendered HTML, JS, CSS and practice files | Cloudflare Static Assets, with pages prerendered at build time | Public: no sign-in, holds only content that may be public |
| Worker API | Redirects and 404s, schema validation, identity and role checks, scoring, data reads and writes | Cloudflare Workers, Hono, Zod | Trust boundary: every authorization and scoring decision is made here |
| D1 database | Stores accounts, sign-in sessions, course sessions, progress, submissions and assessment results | Cloudflare D1, versioned migrations | Trusted persistent data: reachable only through the Worker |
| R2 file storage | Two private buckets: credential evidence files (each file's metadata is kept separately in D1) and pre-built teacher slide exports | Cloudflare R2 | Trusted persistent data: reachable only through the Worker; private and assessor-only evidence is downloaded with time-limited signed URLs, and teacher exports are streamed only to authorized teachers, with each attempt audited |
| Email delivery | Sends transactional email such as account verification, password reset and credential policy-change notices | Cloudflare Workers' built-in send-email capability | Only the Worker can trigger it; the content is decided on the server, never by the front end |
| Structured content | The text and structure of courses, the Help Center and the docs | TypeScript content modules in version control | Public content enters the site at build time; protected content ships only with the Worker |
Design principles
Behind the technology choices above are six principles that the code and the build actually enforce. The table below adds why each principle matters, what it means in practice, and the trade-off that comes with adopting it.
| Principle | Why | Consequence | Trade-off |
|---|---|---|---|
| Public by default | Course content, Help and Docs can be read and indexed by search engines without signing in or running JavaScript, which matters most for a first-time visitor who has not registered yet. | Public pages must be fully renderable as HTML at build time; content and layout must make sense with no interactive state. | Anything designed to be public must be treated as visible to anyone; anything that needs protection has to start out in a protected, server-side-only source, not be hidden by the front end after the fact. |
| Server-authoritative | Anyone can inspect and change the code running in a browser, so only a place a user cannot tamper with can hold an authoritative decision, such as scoring or authorization. | Every operation that needs authorization or scoring is checked again once the Worker receives the request, never trusted from the front end alone. | Every protected action costs an extra round trip to the Worker; the front end cannot show a final result from local state alone and has to wait for the server response. |
| Single source of truth | The same fact defined in two places eventually drifts apart, whether that is a URL, a content ID or the course catalog. | Routing, redirects, 404s, prerendered pages and the sitemap all come from one route table; the course catalog and content each have one definition. | Changing this shared definition has a wider blast radius and needs more careful verification, not a quick edit in just the one place that uses it. |
| Reproducible | Maintainers need to be able to rebuild the whole site and the database schema from source, rather than depending on state left behind by a one-off manual change. | Content, schemas and database migrations all live in version control, so every change is traceable and can be rebuilt. | The database changes only through a new migration; production tables cannot be edited directly, which adds a step even under time pressure. |
| Progressive enhancement | It keeps public content readable even if JavaScript fails to load or is turned off, which helps both usability and reliability. | Public pages need a build-time process that produces complete HTML; interactive features must be layered on top of readable content, not replace it. | The build pipeline gets more complex, since it has to maintain both a "readable as delivered" state and a "interactive after load" state; some interactive experiences need a separate implementation path. |
| Least exposure | Leaving one extra piece of data that should not be public in the output is one more leak risk, especially for answer keys and instructor-only content. | Answer keys, speaker notes and admin logic live only in protected, server-side-only source and never get bundled into the public front end; every build scans the public output. | If the build scan finds a leak, the build fails, and the developer has to fix the leak before deploying, even under a deadline. |
Quality Attributes
These are concrete scenarios rather than abstract adjectives, describing how the platform actually behaves in a few quality dimensions. Each one can be checked against the code or a test; none of them is an unmeasured numeric target.
- 1Accessibility
Even if client-side JavaScript fails to run, public course content remains fully readable, because it is already HTML generated at build time (see "Progressive enhancement"). - 2Security
Modifying the code running in the browser cannot let a learner change an authoritative authorization decision or an assessment score; those decisions are made only in the Worker, see "Trust boundaries". - 3Privacy
Public pages and the front-end bundle never contain protected assessment answers, instructor-only notes, or unnecessary user data; every build scans all public output files and fails if it detects a leak. - 4Reliability
When the database is unavailable, public learning content that was already prerendered is still served through Static Assets; only interactive features that need to read or write the database (such as submitting an assessment) are affected, see "Failure and degradation" below. - 5Maintainability
Changes to URLs, routing and the database schema are reviewed in version control; one route table produces client routing, redirects, 404s and the sitemap, so production behavior and documentation cannot drift apart. - 6Reproducibility
Given the same source revision and a supported toolchain, a maintainer can rebuild the entire public site and reconstruct the database schema by applying the versioned migrations in order. - 7Internationalization
Switching language keeps you on the same logical page; because course, docs and assessment content share the same stable IDs across both languages, automated tests check that section structure, links and IDs stay in parity between them.
Content and data
“Content” and “user data” are different things, stored and managed separately. Editing course text never touches learner data, and nothing learners do is written back into the content.
Structured content
- Courses, the Help Center and these docs
- Kept in version control, not in a separate content management system
- Reviewed, then published with each build
- Stable IDs shared by both languages, with automated structure checks
- The web version is the only authoritative source; print and slide files are derived
User data
- Accounts, sign-in sessions, course sessions and learner codes
- Synced progress, assessment answers and results
- Submissions such as Use Cases, Canvases and Exit Tickets
- Stored in D1 and read or written only through the Worker API
- Its structure changes only through versioned migrations
Learning progress in your browser is a third kind: it stays on your device unless you choose to sync it to a course session.
Course feedback components
Course feedback consists of a browser form, four groups of Worker APIs, D1 tables, schedules and mail delivery, all inside the existing trust boundary: authorization, masking, thresholds and audit happen in the Worker.
| Part | What it is |
|---|---|
| Form page | /feedback/:course, one page in three sections with a progress bar; drafts stay in the browser; the instructor closing slide and QR code are generated in the browser |
| Learner API | /api/feedback: read and write your own feedback, courses still waiting for feedback, anonymous form events, and the live session-code check (session-check) |
| Instructor and HR APIs | /api/teacher-feedback (statistics per session) and /api/org-feedback (organization statistics, comments, CSV, authorized per organization) |
| Admin API | /api/feedback-admin: comment moderation and form performance; retention uses the existing /api/retention-admin; session course binding uses /api/admin/sessions (with an impact preview) |
| Projector QR page | /teacher/feedback-qr: the instructor picks a session and shows its own QR code, a countdown and an A4 print layout full screen; the link carries ?session= so a scan fills the session in |
| D1 tables | Feedback, comments (original and masked text), one-time reminder records and anonymous form-event counters; the HR role lives in the organization membership table |
| Schedules | A monthly retention report (report only) and a daily one-time reminder email (off in production by default) |
| Decks | The closing page of the last module's instructor deck is produced by the offline export script and published to the teacher-export R2 bucket |
Trust boundaries
| Zone | Contains | Who can reach it | How it is protected |
|---|---|---|---|
| Public | HTML, JS, CSS, public course content, practice files, Help and Docs | Anyone, no sign-in | Holds only publishable content; every build scans it for leaks |
| Server only | Answer keys and scoring logic, role and authorization checks, speaker notes and instructor-only content | Only the Worker’s code | Never bundled into the site; served only through protected APIs to authorized roles |
| Persistent data | D1: accounts, sign-in sessions, progress, submissions, assessment results | Only through the Worker API | Schema-validated input, parameterized queries, deny-by-default permissions |
Warning
Deployment
NTPU AI Learn has three deployment environments, each responsible for a different stage of development and release.
User browser
- Public page requests leads to → Static Assets
- API requests (/api/) leads to → Worker Runtime
Static Assets
Prerendered public pages
Worker Runtime
Runs the API, validation and authorization
- Upload and signed download leads to → R2 file storage
- Read and write leads to → D1 database
R2 file storage
Evidence files and teacher exports
D1 database
| Environment | Purpose |
|---|---|
| Production | Serves live traffic |
| Staging | Integration and verification before a production release |
| Local development | Development, testing and debugging |
Production and Staging use independent deployments and data. Local development uses locally simulated resources and never touches production data directly.
Failure & Degradation
This covers only the scenarios that can currently be verified against the architecture and the code's actual behavior. No documented rollback or incident procedure for a failed migration, or for the identity/session service itself degrading, was found in this repository, so no unverified procedure is listed here.
- 1Static delivery available, Worker API unavailable
Public course content keeps reading normally through Static Assets, unaffected. Interactive operations that need the Worker (signing in, submitting an assessment, syncing progress) fail and show a clear error rather than pretending to succeed. - 2Worker API available, D1 unavailable
Public content is unaffected, since it is served from Static Assets and never reads the database. Interactive operations that need to read or write the database fail, and never return a false success. - 3Worker API available, R2 file storage unavailable
Public content and other operations that do not involve files are unaffected. Uploading or downloading credential evidence files, or downloading teacher slide exports, fails and shows a clear error, and never returns a false success. - 4JavaScript fails to execute
Public course content remains readable, because it is HTML generated at build time. Only the interactions React adds after it loads (such as the language switch, self-checks, or marking a lesson complete) become unavailable.
URLs and language
The platform has a single route table: client routing, the Worker’s redirects and 404s, prerendered pages and the sitemap all come from one definition. The table also puts every URL in one of three classes: public content (prerendered and indexable), public interactive workflows (not prerendered, not indexed) and protected pages that require authorization.
| URL | Language | Examples |
|---|---|---|
| No language prefix | Traditional Chinese (default) | /courses, /docs/architecture |
| Under /en/ | English | /en/courses, /en/docs/architecture |
| Legacy /zh/ URLs | Permanently redirected to the unprefixed Chinese URL | /zh/courses → /courses |
The URL decides the language, and switching languages keeps you on the same logical page. Because both languages share the same IDs, progress and links keep working when you switch.
Architecture Decisions
Architecturally significant decisions are kept as Architecture Decision Records (ADRs), each recording its context, the approach taken and its consequences.
ADRs are engineering documents kept in the repository, for anyone who needs a deeper understanding of the implementation or is joining development.
Related documentation
- Security overview: server-side authorization, scoring, and input and output handling
- Privacy: what stays in your browser and what is sent to the server
- Roles and permissions: what learners, instructors and admins can each do
- Bilingual design: URLs, stable IDs and content parity
Was this article helpful?
Feedback is anonymous: only a daily count per article, language and answer is kept.
Related articles
- Platform OverviewWhat NTPU AI Learn is, who it is for, and where public content ends and protected content begins.
- How Progress WorksWhich activities count, where progress is stored, what sync sends, and when progress can be lost.
- AccessibilityThe accessibility behavior the platform implements and tests, and what we aim for.